javajsonpolymorphismjackson

Use property of parent object to determine subclass when deserializing?


children: [
      {
          o kind: "t3"
            data: {                 // ExampleNodeT3 class should be used for kind == t3
                + t3var1: "val1"
                + t3var2: true
            }
      }
      {
          o kind: "t4"                
            data: {                 // ExampleNodeT4 class should be used for kind == t4
                + t4var1: false
                + t4var2: 2346
            }
      }
] ... etc.


@JsonTypeInfo(use=Id.NAME, property="kind")
@JsonSubTypes({
@Type(value=ExampleNodeT3.class, name="t3"),
@Type(value=ExampleNodeT4.class, name="t4")})
public abstract class ExampleNode {
...
public void setData(ExampleNode data) {
    this.data = data;
}

When attempting to deserialize this with Jackson, the JsonTypeInfo hints fail when ExampleNode data is created because the "kind" property is associated with its parent and not visible. I have tried various variations of factory methods, and Jackson annotations, but because Jackson creates the ExampleNode object and passes it to setData() itself, I see no place to control what class of object is created.


Solution

  • Got to here from google , and found the solution. actually these days its possible due to the include=JsonTypeInfo.As.EXTERNAL_PROPERTY , example:

     public class Parent {
    
        @JsonProperty("type")
        public String type;
    
        @JsonProperty("data")
        @JsonInclude(Include.NON_NULL)
        public ChildBase ChildBase;
        
        public Parent() {
            medias = new HashMap<>();
        }
            
        @JsonTypeInfo(use=JsonTypeInfo.Id.CLASS, include=JsonTypeInfo.As.EXTERNAL_PROPERTY, property="type")
        @JsonTypeIdResolver(ChildBaseByParentTypeResolver.class)
        public void setChildBase(ChildBase ChildBase){
            this.ChildBase = ChildBase;
        }
    }
    
    
    @JsonInclude(JsonInclude.Include.NON_NULL)
    @JsonIgnoreProperties(ignoreUnknown = true)
    public class ChildBase {
          public String someStr;
    
    }
    
    
    @JsonInclude(JsonInclude.Include.NON_NULL)
    @JsonIgnoreProperties(ignoreUnknown = true)
    public class AggressiveChild extends ChildBase{
         public String someStr1;
    
    }
    
    @JsonInclude(JsonInclude.Include.NON_NULL)
    @JsonIgnoreProperties(ignoreUnknown = true)
    public class ChilledChild extends ChildBase{
         public String someStr1;
    
    }
    
    
    public class ChildBaseByParentTypeResolver extends TypeIdResolverBase {
        
        private JavaType superType;
    
        @Override
        public void init(JavaType baseType) {
            superType = baseType;
        }   
        
        @Override
        public Id getMechanism() {
            return Id.NAME;
        }
        
        @Override
        public JavaType typeFromId(DatabindContext context, String id) {
            Class<?> subType = ChildBase.class;     
            switch (id) {
            case "agressiveParent":        
                subType = AggressiveChild.class;
                break;
            case "chilledParent":        
                subType = ChilledChild.class;
                break;        
            }
            return context.constructSpecializedType(superType, subType);        
        }
    
        @Override
        public JavaType typeFromId(String directiveType) {
             throw new NotImplementedException();
        }
    
    }
    

    Interesting articles:

    Polymorphism with jackson friend

    Related stack overflow question