Timur Akanaev
Timur Akanaev

Reputation: 43

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

Upvotes: 1

Views: 2706

Answers (3)

Ravi MCA
Ravi MCA

Reputation: 2621

Just try this can be any help

ObjectMapper mapper = new ObjectMapper();

String json = "{ \"commonField\" : \"...\", \"customField\"}";

JsonNode jsonNode = mapper.readTree( json );

A a; // class a

if(jsonNode.has( "customField" )) {

    a = mapper.convertValue( jsonNode, C.class );           
} else {

    a = mapper.convertValue( jsonNode, B.class );
}

Upvotes: 2

jseashell
jseashell

Reputation: 755

I find deserializing into a Map() and iterating over the keys to be pretty simple, especially with the Jackson API

ObjectMapper mapper = new ObjectMapper();
Map<String, String> map = mapper.readValue(jsonString, Map.class);
for(String key : map.keySet)
{
    if(key.equals("customField")
    {
        // do your stuff
    }
}

Upvotes: 1

Vlad Bochenin
Vlad Bochenin

Reputation: 3072

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);
}

Upvotes: 0

Related Questions