在Java的序列化与反序列化过程中,Jackson库提供了强大的功能来处理复杂的数据类型,尤其是在处理多态时。本文将详细探讨@JsonTypeInfo
和@JsonSubTypes
注解的使用,并通过一个实例来展示如何在Jackson中使用这些注解。
背景知识
在JSON序列化过程中,如果使用Java类的全限定名作为类型标识符,这在非Java客户端消费序列化后的JSON时可能不是一个好的选择。为了解决这个问题,我们可以使用"逻辑类型名称"作为类型标识符。
使用@JsonTypeInfo与@JsonSubTypes
@JsonTypeInfo
注解用于指定如何序列化和反序列化类型信息。当设置use = JsonTypeInfo.Id.NAME
时,类型名称将被用作类型标识符。为了将这个名称解析为实际的具体类型,我们需要使用@JsonSubTypes
注解。
实例分析
假设我们有一个Shape
抽象类,它有两个子类Rectangle
和Circle
。我们希望在序列化时使用逻辑类型名称,而不是Java类的全限定名。
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY)
@JsonSubTypes({
@JsonSubTypes.Type(value = Rectangle.class),
@JsonSubTypes.Type(value = Circle.class)
})
public abstract class Shape { }
@JsonTypeName("rectangle")
public class Rectangle extends Shape {
private int w;
private int h;
// 构造函数和getter/setter省略
}
@JsonTypeName("circle")
public class Circle extends Shape {
private int radius;
// 构造函数和getter/setter省略
}
public class View {
private List<Shape> shapes;
// 构造函数和getter/setter省略
}
序列化与反序列化示例
在ExampleMain
类中,我们创建了一个View
对象,其中包含Rectangle
和Circle
对象的列表。然后使用ObjectMapper
进行序列化和反序列化。
public class ExampleMain {
public static void main(String[] args) throws IOException {
View v = new View();
v.setShapes(new ArrayList<>(List.of(Rectangle.of(3, 6), Circle.of(5))));
System.out.println("-- serializing --");
ObjectMapper om = new ObjectMapper();
String s = om.writeValueAsString(v);
System.out.println(s);
System.out.println("-- deserializing --");
View view = om.readValue(s, View.class);
System.out.println(view);
}
}
输出结果
序列化输出:
{"shapes":[{"@type":"rectangle","w":3,"h":6},{"@type":"circle","radius":5}]}
反序列化输出:
View{shapes=[Rectangle{w=3, h=6}, Circle{radius=5}]}
自定义类型属性名称
如果我们想要自定义类型属性的名称,而不是使用默认的@type
,可以通过property
元素来指定。
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "typeName")
...
这将序列化输出为:
{"shapes":[{"typeName":"rectangle","w":3,"h":6},{"typeName":"circle","radius":5}]}
使用类型包装器
除了JsonTypeInfo.As.PROPERTY
,我们还可以使用JsonTypeInfo.As.WRAPPER_OBJECT
或JsonTypeInfo.As.WRAPPER_ARRAY
来包装类型信息。
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.WRAPPER_OBJECT)
...
这将序列化输出为:
{"shapes":[{"rectangle":{"w":3,"h":6}},{"circle":{"radius":5}}]}
项目依赖和技术
- jackson-databind 2.9.6: Jackson的核心数据绑定功能。
- JDK 10
- Maven 3.3.9
通过本文的分析,我们可以看到@JsonTypeInfo
和@JsonSubTypes
在处理多态和类型安全方面的重要作用。希望这个实例能够帮助你更好地理解和使用这些注解。