javajacksonjsonschemapojojsonschema2pojo

Json schema with anyOf fields to POJO


I wonder what's the recommended way to generate POJOs for Json schemas with "anyOf" fields?

For example, given the following json schemas:

hobby.json
{
    "anyOf": [
        { "type": {"$ref": "./exercise.json" } },
        { "type": {"$ref": "./music.json" } }
    ]    
}
exercise.json
{
    "type": "object"
    "properties" {
        "hobbyType": {"type": "string"}
        "exerciseName": { "type": "string" },
        "timeSpent": { "type": "number" },
        "place": { "type": "string" }
    }
}
music.json
{
    "type": "object"
    "properties" {
        "hobbyType": {"type": "string"}
        "instrument": { "type": "string" },
        "timeSpent": { "type": "number" }
    }
}

How could I generate a POJO for Hobby.java with Jackson?


Solution

  • The approach I ended up taking is using polymorphic marshaling/unmarshaling functionality provided by Jackson.

    Specifically -

    1. Make hobby to be an interface and annotate it with @JsonTypeInfo and @JsonSubTypes
    @JsonTypeInfo(
        use = JsonTypeInfo.Id.NAME,
        property = "hobbyType",
        include = JsonTypeInfo.As.EXISTING_PROPERTY,
        visible = true
    )
    @JsonSubTypes({
            @Type(value = Exercise.class, name = "exercise"),
            @Type(value = Music.class, name = "music")
    })
    public interface Hobby {
    }
    
    1. Create Exercise.java and Music.java that implements this interface
    @Builder
    @Data
    @AllArgsConstructor
    public class Exercise implements Hobby {
        @JsonProperty("hobbyType")
        @Builder.Default
        @NonNull
        private final String hobbyType = "exercise";    
    
        @JsonProperty("exerciseName")
        private String exerciseName;
    
        @JsonProperty("place")
        private String place;
        //... (other fields)
    }
    
    1. Use Hobby for serialization and deserialization.
    // create a Hobby object
    Hobby exercise = Exercise.builder().exerciseName("swimming").place("swimmingPool").build();
    
    // serialization
    String serializedHobby = new ObjectMapper.writeValueAsString(exercise)
    /**
    serializedHobby looks like this ->
    {
        "hobbyType": "exercise",
        "exerciseName": "swimming",
        "place": "swimmingPool"
    }
    */
    
    // deserialization
    Hobby deserializedObject = new ObjectMapper.readValue(jsonString, Hobby.class)
    // deserializedObject.getClass() would return Exercise.java or Music.java based on the hobbyType
    
    

    Ref: https://www.baeldung.com/jackson-inheritance