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