In the last two tutorials we used @JsonTypeInfo with use = JsonTypeInfo.Id.CLASS which causes fully-qualified Java class name to be serialized as the type identifier. Java class name may not be a good choice when the serialized JSON is consumed by non-Java clients. In that case we can use "logical type name" as use = JsonTypeInfo.Id.NAME. In that case name will then need to be separately resolved to actual concrete type (Class) via @JsonSubTypes and @JsonTypeName annotations.
@JsonTypeName
It is used for binding logical name that the annotated class has. For example:
@JsonTypeName("rectangle")
public class Rectangle extends Shape {
...
}
@JsonSubType
Defines subtypes along with an optional name. It is used with @JsonTypeInfo . For example:
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY)
@JsonSubTypes( @JsonSubTypes.Type(value = Rectangle.class, name = "rectangle"))
public abstract class Shape {
...
}
If name is missing, class of the type will be checked for @JsonTypeName annotation, and if that is also missing or empty, a default name will be constructed. Default name is usually based on class name.
Example
Java Objects
@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;
.............
}
@JsonTypeName("circle")
public class Circle extends Shape {
int radius;
.............
}
public class View {
private List<Shape> shapes;
.............
}
Serializing and deserializing
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);
}
} -- serializing -- {"shapes":[{"@type":"rectangle","w":3,"h":6},{"@type":"circle","radius":5}]} -- deserializing -- View{shapes=[Rectangle{w=3, h=6}, Circle{radius=5}]}
Specifying 'property'
In above example the type 'name' is serialized with property "@type" which is the default when use=JsonTypeInfo.Id.NAME is used. To change that we can use property element. For example
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "typeName")
...
which will serialize to:
{"shapes":[{"typeName":"rectangle","w":3,"h":6},{"typeName":"circle","radius":5}]}
Using a type wrapper instead of JsonTypeInfo.As.PROPERTY
We can also specify use = JsonTypeInfo.Id.NAME with include = JsonTypeInfo.As.WRAPPER_OBJECT or include = JsonTypeInfo.As.WRAPPER_ARRAY (see last tutorial). In that case we don't need to specify property element. For example
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.WRAPPER_OBJECT)
....
which will serialize to:
{"shapes":[{"rectangle":{"w":3,"h":6}},{"circle":{"radius":5}}]}
Example ProjectDependencies and Technologies Used: - jackson-databind 2.9.6: General data-binding functionality for Jackson: works on core streaming API.
- JDK 10
- Maven 3.3.9
|