Reputation: 12665
I have this parent class:
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "objectType")
@JsonSubTypes(
{
@JsonSubTypes.Type(value = Child.class, name = "child"),
}
)
public class Parent {
@JsonProperty("objectType")
private ProductType productType
public ProductType getProductType { return productType; }
}
This class is extended by a child class:
public class Child extends Parent {
@JsonProperty("field1")
private Field1 field1;
@JsonProperty("field2")
private Field2 field2;
}
I try to parse the following Json file representing a serialized instance of Child
class:
{
"objectType" : "child1",
"field1" : "value1",
"field2" : "value2"
}
... with the following code:
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.enableDefaultTyping();
Parent parent = objectMapper.treeToValue(parentJsonNode, Parent.class);
(p.s. parentJsonNode
is a JsonNode
created by reading a JSON file).
What happens is that the field1
and field2
of my object are correctly read and set, however the field objectType
(declared into the parent class) remains null
after parsing.
What should I do to make Jackson read the value child
of objectType
correctly from the Json object?
Here is the code for the ProductType
:
@JsonFormat(shape = JsonFormat.Shape.OBJECT)
public enum ProductType {
CHILD1("child1"), CHILD2("child2");
private String objectType;
private ProductType(String objectType) {
this.objectType = objectType;
}
@JsonValue
public String getObjectType() { return objectType; }
}
Upvotes: 0
Views: 1961
Reputation: 18143
This is due to the strange behaviour of Jackson to add an additional field non-explicitly visible to you during compile time to distinguish between the subclasses named like the value you put in @JsonTypeInfo
. Because your parent class contains a property serialized into a JSON property named objectType
and the name of the denominator given in @JsonTypeInfo
also being objectType
. If you would serialize an object of Child
you would notice that the serialized object contains two fields with identical name objectType
where only one of them would be filled:
System.out.println(objectMapper.writeValueAsString(new Child()));
leads to
{"objectType":"child","objectType":null,"field1":null,"field2":null}
The workaround is to either skip the explicit field productType
(serialized as objectType
) in the parent class if you don't need it in your code, or give it a different name for serialization (e.g. productType
), but then you need to of course have a second JSON field to be deserialized into it:
Changing
public class Parent {
@JsonProperty("objectType")
private String productType;
to
public class Parent {
@JsonProperty("productType")
private String productType;
and
Parent parent = objectMapper.treeToValue(objectMapper.readTree("{\n" +
" \"objectType\" : \"child\",\n" +
" \"productType\" : \"child\",\n" +
" \"field1\" : \"value1\",\n" +
" \"field2\" : \"value2\"\n" +
"}"), Parent.class);
System.out.println(parent.getProductType());
leads to the output
child
A third possibility would be to initialize the field explicitly, e.g. via constructor, because Jackson uses the default constructor to create the objects on deserialization:
// For subclasses
protected Parent (String productType) {
this.productType = productType;
}
// For Jackson
public Parent () {
}
and
public Child () {
super("child");
}
Edit: I used String
instead of ProductType
to ease up my code but the result is similar.
Upvotes: 2