I want to do something like this with subtypes. I have 3 different types of objects:
{ "value": "string" } { "value": { "type": "obj1" } } { "value": { "type": "obj2" } }
value
can either be a string or an object.
The corresponding Java classes are
public interface Value { } public class ValueString implements Value { String value; } public abstract class ValueObj implements Value{ public String type; } public class ValueObj1 extends ValueObj { private Obj1 value; } public class ValueObj2 extends ValueObj { private Obj2 value; }
I don’t mind having a discriminator inside Obj1 and Obj2, but there is no place for one when the value is just a string. Is there a way that I can set this up so that if the value is a string, it deserializes to ValueString, but if it is an object, it deserializes to the correct ValueObj1 or ValueObj2?
Advertisement
Answer
It can be easily done by creating a custom deserializer first:
p.s. I assumed that there’re only three types of objects as you posted.
public class ValueDeserializer extends StdDeserializer<Value> { public ValueDeserializer() { this(null); } protected ValueDeserializer(Class<?> vc) { super(vc); } @Override public Value deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException, JacksonException { JsonNode jsonNode = jsonParser.getCodec().readTree(jsonParser); if (jsonNode.get("value").isValueNode()) { return new ValueString(jsonNode.get("value").asText()); } else if ("obj1".equals(jsonNode.get("value").get("type").asText())) { ValueObj1 valueObj1 = new ValueObj1(); // The logic to handle type obj1 return valueObj1; } else { ValueObj2 valueObj2 = new ValueObj2(); // The logic to handle type obj2 return valueObj2; } }
Then simply annotate class Value
with @JsonDeserialize
as follows:
@JsonDeserialize(using = ValueDeserializer.class) public interface Value { }
Finally, let Jackson
do the rest for you:
ObjectMapper objectMapper = new ObjectMapper(); System.out.println(objectMapper.writeValueAsString(objectMapper.readValue(jsonStr, Value.class)));