Reputation: 412
I have a setup like this:
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "dishName", defaultImpl = Food.class)
@JsonSubTypes(value = {
@Type(name = "fries", value = Fries.class),
@Type(name = "burger", value = Burger.class)
})
public class Food {
private String dishName;
@Override
public String toString() {
return dishName + ", type: " + this.getClass().getName();
}
}
public class Fries extends Food { /*...*/ }
public class Burger extends Food { /*...*/ }
public class TryItOut {
private static String foodString = "[ { \"dishName\":\"burger\" }, { \"dishName\":\"fries\" }, { \"dishName\":\"cabbage\" } ]";
public static void main(String[] args) {
ObjectMapper m = new ObjectMapper();
try {
Food[] food = m.readValue(foodString, Food[].class);
for (Food dish : food) {
System.out.println(dish);
}
} catch (IOException e) {
System.out.println("something went wrong");
e.printStackTrace();
}
}
}
I want to use this to deserialize json whiches content I cannot influence (so no option to add "proper" type information). The problem I have is that apparently the dishName
json property is used to determine the subtype all right, but it is not deserialized into the java field. Is there a way to achieve that, too? In other words: the main method prints
null, type: Burger
null, type: Fries
null, type: Food
on the console, but I want it to print
burger, type: Burger
fries, type: Fries
cabbage, type: Food
this is particularly nasty because there is no way for me to find out later on that the last object is cabbage. That nullifies the benefits of the default implementation.
EDIT:
@Evil Raat 's answer does the trick. For the sake of completeness: the dishName
field in the Food
class needs the @JsonProperty
Annotation for this example to work. The working example thus looks like this:
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "dishName", defaultImpl = Food.class, visible = true)
@JsonSubTypes(value = {
@Type(name = "fries", value = Fries.class),
@Type(name = "burger", value = Burger.class)
})
public class Food {
@JsonProperty
private String dishName;
@Override
public String toString() {
return dishName + ", type: " + this.getClass().getName();
}
}
public class Fries extends Food { /*...*/ }
public class Burger extends Food { /*...*/ }
public class TryItOut {
private static String foodString = "[ { \"dishName\":\"burger\" }, { \"dishName\":\"fries\" }, { \"dishName\":\"cabbage\" } ]";
public static void main(String[] args) {
ObjectMapper m = new ObjectMapper();
try {
Food[] food = m.readValue(foodString, Food[].class);
for (Food dish : food) {
System.out.println(dish);
}
} catch (IOException e) {
System.out.println("something went wrong");
e.printStackTrace();
}
}
}
Upvotes: 9
Views: 4190
Reputation: 123
Fix provided above was not working for me because initial serialization was producing duplicating field with null.
var str = objectMapper.writeValueAsString(foodDto);
// { \"dishName\":\"burger\", ..., \"dishName\":null }
So you need to annotate field like this
@JsonProperty(value = "dishName", access = JsonProperty.Access.WRITE_ONLY)
private String dishName;
Upvotes: 2
Reputation: 4199
To maintain the value of the property you used for the Deserialization, you just have to set the visible property to true on the @JsonSubTypes annotation:
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "dishName", defaultImpl = Food.class, visible = true)
Upvotes: 13