How to use @JsonTypeInfo, @JsonSubType to determine a field type based on a sibling field's value?

Viewed 1779

Say I have a class structure like this :-

class ShapeRequest {

    ShapeInfo shapeInfo;
    Shape shape;

    static class ShapeInfo {
        String shapeName;
        String shapeDimension;
    }

    static abstract class Shape {

    }

    static class Square extends Shape{
        int area;
    }

    static class Circle extends Shape{
        int area;
    }
}

How can I deserialize the ShapeRequest in a way that field shape, is mapped to Square or Circle type, depending on the shapeInfo.shapeName field value?

For example, the following JSON should map to ShapeRequest with Circle shape type, because shapeInfo.shapeName = "circle"

{
  "shapeInfo": {
    "shapeName": "circle",
    "shapeDimension": "2"
  },
  "shape": {
    "area": 10
  }
}
1 Answers

You can use it below:

public class JsonTypeExample {

    // Main method to test our code.
    public static void main(String args[]) throws IOException {
        ObjectMapper objectMapper = new ObjectMapper();

        // json to circle based on shapeName
        String json = "{\"shapeName\":\"circle\",\"area\":10}";
        Shape shape = objectMapper.readerFor(Shape.class).readValue(json);
        System.out.println(shape.getClass());
        System.out.println(objectMapper.writeValueAsString(shape));

        // json to square based on shapeName
        json = "{\"shapeName\":\"square\",\"area\":10}";
        shape = objectMapper.readerFor(Shape.class).readValue(json);
        System.out.println(shape.getClass());
        System.out.println(objectMapper.writeValueAsString(shape));
    }

    @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "shapeName")
    @JsonSubTypes({
            @JsonSubTypes.Type(value = Square.class, name = "square"),
            @JsonSubTypes.Type(value = Circle.class, name = "circle")
    })
    static class Shape {
        Shape() {
        }
    }

    @JsonTypeName("square")
    static class Square extends Shape {
        public int area;

        Square(int area){
            super();
            this.area = area;
        }
    }

    @JsonTypeName("circle")
    static class Circle extends Shape {
        public int area;

        Circle(int area){
            super();
            this.area = area;
        }
    }
}

Here Shape class is annotated with JsonTypeInfo and JsonSubTypes.

@JsonTypeInfo is used to indicate details of type information which is to be included in serialization and de-serialization. Here property signifies the value to be considered for determining the SubType.

@JsonSubTypes is used to indicate subtypes of types annotated. Here name and value maps the shapeName to appropriate SubType class.

Whenever a JSON is passed as in the example, de-serialization happens via JsonSubTypes, then it gives back the appropriate JAVA object based on shapeName.

Related