java - 动态选择从 json 创建对象的类

标签 java json design-patterns

我有一个有趣的问题,我无法想出一个干净的解决方案。我的应用程序读取 json 对象的集合,它需要根据 json 本身中的字段反序列化为这种或那种类类型。我无法控制 json 结构或它如何到达我的应用程序。

我已经为可能进入应用程序的每种类型的对象创建了模型,并且我已经达到了我试图构建一个拉出“类型”字段然后使用 ObjectMapper 反序列化的服务的地步将 json 转换为适当的模型。

Json 示例:

{
    "message_type" : "model1"
    "other data" : "other value"
    ...
}

模型:

public class Model1 {
    ...
}

public class Model2 {
    ...
}

服务?

public class DynamicMappingService {

    public ???? mapJsonToObject(String json) {
        String type = pullTypeFromJson();

        ???
    }

    private String pullTypeFromJson() {...}
}

我不想要一个巨大的 switch 语句,它说“如果类型值是这个,那么反序列化到那个”,但我正在努力想出一些干净的东西来做到这一点。我想也许是一个通用模型类,其中通用参数是模型类型,唯一的字段是该模型类型的实例,但这似乎也不正确,我不确定这能给我带来什么。我也可以有某种空的抽象类,所有模型都可以扩展它,但这看起来也很糟糕。我该如何处理?举个例子加分。

最佳答案

我使用父接口(interface) Vehicle 的概念,它有两个类 CarTruck。 在您的情况下,这意味着 Model1Model2 应该实现一个通用接口(interface)。

我的测试类:

import com.fasterxml.jackson.databind.ObjectMapper;

public class Tester {
    static ObjectMapper mapper=new ObjectMapper();

    public static void main(String[] args) throws IOException {
        Car car = new Car();
        car.setModel("sedan");
        String jsonCar=mapper.writeValueAsString(car);
        System.out.println(jsonCar);
        Vehicle c=mapper.readValue(jsonCar, Vehicle.class);
        System.out.println("Vehicle of type: "+c.getClass().getName());
        
        Truck truck=new Truck();
        truck.setPower(100);
        String jsonTruck=mapper.writeValueAsString(truck);
        System.out.println(jsonTruck);
        Vehicle t=mapper.readValue(jsonTruck, Vehicle.class);
        System.out.println("Vehicle of type: "+t.getClass().getName());
    }
}

您需要在某处存储类型字段的值与相应类之间的映射。根据您想要的位置,实现会有所不同。

1) 父类型保存子类型列表:

import com.fasterxml.jackson.annotation.JsonSubTypes;
import com.fasterxml.jackson.annotation.JsonTypeInfo;

@JsonSubTypes({
    @JsonSubTypes.Type(value = Car.class, name = "car"),
    @JsonSubTypes.Type(value = Truck.class, name = "truck") }
)
@JsonTypeInfo(
          use = JsonTypeInfo.Id.NAME, 
          include = JsonTypeInfo.As.PROPERTY, 
          property = "type")
public interface Vehicle {
}

CarTruck 的模型是简单的 POJO,没有任何注释:

public class Car implements Vehicle {
    private String model;

    public String getModel() {
        return model;
    }

    public void setModel(String model) {
        this.model = model;
    }
}

2) 一个单独的解析器保存映射:

Vehicle 包含额外的注解 @JsonTypeIdResolver

import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.databind.annotation.JsonTypeIdResolver;

@JsonTypeInfo(
          use = JsonTypeInfo.Id.NAME, 
          include = JsonTypeInfo.As.PROPERTY, 
          property = "type")
@JsonTypeIdResolver(JsonResolver.class)
public interface Vehicle {
}

JsonResolver 类 保存类型字段值和类之间的映射:

import java.util.HashMap;
import java.util.Map;

import com.fasterxml.jackson.annotation.JsonTypeInfo.Id;
import com.fasterxml.jackson.databind.DatabindContext;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.jsontype.impl.TypeIdResolverBase;

public class JsonResolver extends TypeIdResolverBase {

    private static Map<String,Class<?>> ID_TO_TYPE=new HashMap<>();
    static {
        ID_TO_TYPE.put("car",Car.class);
        ID_TO_TYPE.put("truck",Truck.class);
    }
    public JsonResolver() {
        super();
    }

    @Override
    public Id getMechanism() {
        return null;
    }

    @Override
    public String idFromValue(Object value) {
        return value.getClass().getSimpleName();
    }

    @Override
    public String idFromValueAndType(Object value, Class<?> arg1) {
        return idFromValue(value);
    }

    @Override
    public JavaType typeFromId(DatabindContext context, String id) {
        return context.getTypeFactory().constructType(ID_TO_TYPE.get(id));
    }
}

3) json 包含完整的类名:

如果您接受序列化的 json 包含完整的 java 类名,则不需要解析器但指定 use = JsonTypeInfo.Id.CLASS:

import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.databind.annotation.JsonTypeIdResolver;

@JsonTypeInfo(
          use = JsonTypeInfo.Id.CLASS, 
          include = JsonTypeInfo.As.PROPERTY, 
          property = "type")
public interface Vehicle {
}

解决方案 3 是最容易实现的,但我个人不喜欢在我的数据中包含完整的 java 类名的想法。如果您开始重构您的 Java 包,这可能是一个潜在的风险。

关于java - 动态选择从 json 创建对象的类,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57102092/

相关文章:

java - 在不破坏封装的情况下保存和加载对象

java - jks keystore 中的证书将导入到 java home 中

java - this的返回值,进一步解释

java - REST 样式 URL 的首选方法?

php - jQuery 获取 JSON 网址

将字符模式再现为数字模式

java - 在 mysql(或 JPA)中插入多行

java - 如何使SWT中的任何对象变得模糊?

php - 在 Android 和 PHP Web 服务之间验证用户

design-patterns - 根据用户权限隐藏/禁用 GUI 元素的最佳方法?