Search code examples
javajsonjacksondeserializationjson-deserialization

Jackson: choose children class for deserialization depending of field existence


I have the following model:

public abstract class A {
    String commonField;
}

public class B extends A {    
}

public class C extends A {
    String customField;
}

How to tell choose class (B or C) depending of existence 'customField' field?

{ "commonField" : "..."} --> B instance

{ "commonField" : "...", "customField" : null} --> B instance

{ "commonField" : "...", "customField" : "..."} --> C instance

Solution

  • You can write own deserializer for your case and register it in ObjectMapper with additional module or with @JsonDeserialize annotation on A.

    For example:

    public class ADeserializer extends StdDeserializer<A> {
        public ADeserializer() {
            super(A.class);
        }
    
        @Override
        public A deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JsonProcessingException {
            JsonNode node = p.readValueAsTree();
            JsonNode customField = node.findValue("customField");
            A result;
            if (customField != null && !customField.isNull()) {
                result = new C();
                ((C)result).customField = customField.asText();
            } else {
                result = new B();
            }
            result.commonField = node.findValue("commonField").asText();
            return result;
        }
    }
    

    and using with new module registration:

    @Test
    public void test() throws Exception {
        ObjectMapper mapper = new ObjectMapper();
        SimpleModule module = new SimpleModule();
        module.addDeserializer(A.class, new ADeserializer());
        mapper.registerModule(module);
    
        String jsonB = "{\"commonField\" : \"value\"}";
        Assert.assertTrue(mapper.readValue(jsonB, A.class) instanceof B);
        String jsonBNull = "{\"commonField\" : \"value\", \"customField\" : null}";
        Assert.assertTrue(mapper.readValue(jsonBNull, A.class) instanceof B);
        String jsonC = "{\"commonField\" : \"value\", \"customField\" : \"anotherValue\"}";
        Assert.assertTrue(mapper.readValue(jsonC, A.class) instanceof C);
    }