Close

Jackson Json - @JsonTypeInfo, using Logical Type Name with @JsonSubTypes and @JsonTypeName

[Updated: Jun 28, 2018, Created: Jun 14, 2018]

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 Project

Dependencies 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

@JsonTypeInfo with @JsonSubTypes and @JsonTypeName Example Select All Download
  • jackson-json-type-info-with-json-type-name
    • src
      • main
        • java
          • com
            • logicbig
              • example
                • Shape.java

    See Also