b15*_*b15 5 java json design-patterns
我有一个有趣的问题,我在想出一个干净的解决方案时遇到了麻烦。我的应用程序读取 json 对象的集合,它需要根据 json 本身中的字段将其反序列化为这个或那个类类型。我无法控制 json 结构或它如何到达我的应用程序。
我已经为可能进入应用程序的每种类型的对象创建了模型,并且我已经到了尝试构建一个服务的地步,该服务拉出“类型”字段,然后使用 ObjectMapper 将 json 反序列化为合适的型号。
JSON 示例:
{
"message_type" : "model1"
"other data" : "other value"
...
}
Run Code Online (Sandbox Code Playgroud)
楷模:
public class Model1 {
...
}
public class Model2 {
...
}
Run Code Online (Sandbox Code Playgroud)
服务?:
public class DynamicMappingService {
public ???? mapJsonToObject(String json) {
String type = pullTypeFromJson();
???
}
private String pullTypeFromJson() {...}
}
Run Code Online (Sandbox Code Playgroud)
我不想要一个大量的 switch 语句,它说“如果类型值是这个,那么反序列化到那个”,但我正在努力想出一些干净的东西来做到这一点。我想可能是一个通用模型类,其中通用参数是模型类型,唯一的字段是该模型类型的实例,但这似乎也不对,我不确定这对我有什么好处。我也可以有一些所有模型都扩展的空抽象类,但这看起来也很糟糕。我该如何处理?举例加分。
我使用父接口Vehicle的概念,其中包含 2 个类Car和Truck。在您的情况下,这意味着Model1和Model2应该实现一个通用接口。
我的测试课:
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());
}
}
Run Code Online (Sandbox Code Playgroud)
您需要在某个地方存储类型字段的值和相应类之间的映射。根据您想要的位置,实施方式有所不同。
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 {
}
Run Code Online (Sandbox Code Playgroud)
汽车和卡车的模型是简单的 POJO,没有任何注释:
public class Car implements Vehicle {
private String model;
public String getModel() {
return model;
}
public void setModel(String model) {
this.model = model;
}
}
Run Code Online (Sandbox Code Playgroud)
2) 一个单独的解析器保存映射:
车辆包含额外注释@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 {
}
Run Code Online (Sandbox Code Playgroud)
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));
}
}
Run Code Online (Sandbox Code Playgroud)
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 {
}
Run Code Online (Sandbox Code Playgroud)
解决方案 3是最容易实现的,但我个人不喜欢在数据中包含完整的 java 类名。如果您开始重构 java 包,这可能是一个潜在的风险。