Search code examples
javajacksondeserializationfasterxml

Jackson Polymorphic Deserialization via field


let's say, i have a class

public class A{
  private UUID typeId;
  private B data;
}

public abstract class B{
  private String a;
}

public class BChildOne extends B{
  ... some variables
}

public class BChildTwo  extends B{
  ... some variables
}

type of class B is changing, according to A's typeId , so if typeId of A is "XXX", type of data field is BChildOne, and if typeId of A is "YYY", type of data field is BChildTwo.

how can i achive that?

so for i tried that;

@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility =
JsonAutoDetect.Visibility.NONE, setterVisibility = 
JsonAutoDetect.Visibility.NONE)
@JsonIgnoreProperties(ignoreUnknown = true)
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = 
JsonTypeInfo.As.EXTERNAL_PROPERTY , property = "typeId")
@JsonSubTypes({
 @JsonSubTypes.Type(value = BChildOne.class, name = "40ad2fe6-e672-4f0e- 
986e- 
 619c7a1a3223") }
 )
 public abstract class B{

but i got following error;

Unexpected token (END_OBJECT), expected FIELD_NAME: missing property 'typeId' that is to contain type id (for class B)

which is obvious, because typeId field is in class A not B.


Solution

  • Assuming that your JSON documents are like:

    {
      "type": "foo",
      "data": {
        "someCommonProperty": "common property",
        "fooProperty": "foo specific property"
      }
    }
    
    {
      "type": "bar",
      "data": {
        "someCommonProperty": "common property",
        "barProperty": "bar specific property"
      }
    }
    

    You can use:

    public class Wrapper {
    
        private String type;
    
        @JsonTypeInfo(use = Id.NAME, property = "type", include = As.EXTERNAL_PROPERTY)
        @JsonSubTypes(value = { 
            @JsonSubTypes.Type(value = Foo.class, name = "foo"),
            @JsonSubTypes.Type(value = Bar.class, name = "bar") 
        })
        private AbstractData data;
    
        // Getters and setters
    }
    
    public abstract class AbstractData {
    
        private String someCommonProperty;
    
        // Getters and setters
    }
    
    public class Foo extends AbstractData {
    
        private String fooProperty;
    
        // Getters and setters
    }
    
    public class Bar extends AbstractData {
    
        private String barProperty;
    
        // Getters and setters
    }
    

    In this approach, @JsonTypeInfo is set to use type as an external property to determine the right class to map the data property. The JSON document can be deserialized as following:

    ObjectMapper mapper = new ObjectMapper();
    Wrapper wrapper = mapper.readValue(json, Wrapper.class);