Reputation: 105
I have this JSON documents
1:
{
"type": "first_type",
"configs": [
{
"itemLevel": 1,
"power": {
"firstTypeParam": "xxxx"
}
},
{
"itemLevel": 2,
"power": {
"firstTypeParam": "yyy"
}
}
]
}
2:
{
"type": "second_type",
"configs": [
{
"itemLevel": 11,
"power": {
"anotherParam": true
}
},
{
"itemLevel": 12,
"power": {
"anotherParam": false
}
]
}
A couple of java classes
public class Dto {
String type;
Collection<Config>;
}
public class Config {
int itemLevel;
Collection<Power> powers;
}
public interface Power {}
public class FirstPower implements Power {
String firstTypeParam;
}
public class SecondPower implements Power {
boolean anotherParam;
}
I tried to implement custom jackson deserializer @JsonDeserialize(using = MyStdDeserializer.class"
on top of Power
interface but couldn't find out how to access to neighbor node of the parent with type flag.
Do you know how to fix class hierarchy and/or use jackson features/annotations to deserialize JSON with "first_type"
type onto FirstPower
class and "second_type"
onto SecondPower
?
I'm using jackson 2.9.7 It is possible to change class hierarchy and JSON format little bit and also I have ability to use annotation-based deserialization.
Upvotes: 0
Views: 906
Reputation: 8124
Since the type
information is stored in Dto
class, the custom JsonDeserializer
should be implemented for 'Dto' class instead of 'Power' interface in order to access the type
information. The crucial part of the implementation of the custom JsonDeserializer
in below code is the line
config.powers.add(parser.readValueAs(getPowerClass(dto.type)));
where getPowerClass
method determine the class(FirstPower
or SecondPower
) required by using the type
of dto
. Once the class is known, we can deserialize the power
object simply by calling readValueAs
method. Following classes(should be put in same package) demonstrate how to implement the custom JsonDeserializer
.
Main class
import java.io.IOException;
import com.fasterxml.jackson.core.JsonParseException;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.ObjectMapper;
public class PolymorphicDeserialize {
public static void main(String[] args) throws JsonParseException, JsonMappingException, IOException {
ObjectMapper mapper = new ObjectMapper();
Dto type1 = mapper.readValue(getType1Json(), Dto.class);
Dto type2 = mapper.readValue(getType2Json(), Dto.class);
printDto(type1);
printDto(type2);
}
private static void printDto(Dto dto) {
System.out.println("type :" + dto.type);
for (Config config : dto.configs) {
System.out.println("itemLevel:" + config.itemLevel);
System.out.println("powers:" + config.powers);
}
}
private static String getType1Json() {
return " { "
+ " \"type\": \"first_type\", "
+ " \"configs\": [ "
+ " { "
+ " \"itemLevel\": 1, "
+ " \"power\": { "
+ " \"firstTypeParam\": \"xxxx\" "
+ " } "
+ " }, "
+ " { "
+ " \"itemLevel\": 2, "
+ " \"power\": { "
+ " \"firstTypeParam\": \"yyy\" "
+ " } "
+ " } "
+ " ] "
+ " } ";
}
private static String getType2Json() {
return " { "
+ " \"type\": \"second_type\", "
+ " \"configs\": [ "
+ " { "
+ " \"itemLevel\": 11, "
+ " \"power\": { "
+ " \"anotherParam\": true "
+ " } "
+ " }, "
+ " { "
+ " \"itemLevel\": 12, "
+ " \"power\": { "
+ " \"anotherParam\": false "
+ " } "
+ " } "
+ " ] "
+ " } ";
}
}
Dto class
import java.util.Collection;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
@JsonDeserialize(using = DtoDeserializer.class)
public class Dto {
String type;
Collection<Config> configs;
}
DtoDeserializer class
import java.io.IOException;
import java.util.ArrayList;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.JsonToken;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
public class DtoDeserializer extends JsonDeserializer<Dto> {
@Override
public Dto deserialize(JsonParser parser, DeserializationContext ctxt) throws IOException, JsonProcessingException {
Dto dto = new Dto();
dto.configs = new ArrayList<Config>();
while (parser.nextToken() == JsonToken.FIELD_NAME) {
deserializeType(parser, dto);
deserializeConfigs(parser, dto);
}
return dto;
}
private void deserializeType(JsonParser parser, Dto dto) throws IOException, JsonProcessingException {
if (!"type".equals(parser.getCurrentName())) {
return;
}
parser.nextToken();
dto.type = parser.getValueAsString();
}
private void deserializeConfigs(JsonParser parser, Dto dto) throws IOException, JsonProcessingException {
if (!"configs".equals(parser.getCurrentName())) {
return;
}
if (parser.nextToken() != JsonToken.START_ARRAY) {
return;
}
while (parser.nextValue() != null) {
if (parser.getCurrentToken() != JsonToken.START_OBJECT) {
continue;
}
Config config = new Config();
config.powers = new ArrayList<Power>();
while (parser.nextToken() != JsonToken.END_OBJECT) {
if ("itemLevel".equals(parser.getCurrentName())) {
parser.nextToken();
config.itemLevel = parser.getValueAsInt();
} else if ("power".equals(parser.getCurrentName())) {
parser.nextToken();
config.powers.add(parser.readValueAs(getPowerClass(dto.type)));
}
}
dto.configs.add(config);
}
}
private Class<? extends Power> getPowerClass(String type) {
if ("first_type".equals(type)) {
return FirstPower.class;
} else if ("second_type".equals(type)) {
return SecondPower.class;
}
throw new IllegalArgumentException("Not known type" + type);
}
}
Power interface
public interface Power {}
FirstPower class
public class FirstPower implements Power {
String firstTypeParam;
String getFirstTypeParam() {
return firstTypeParam;
}
void setFirstTypeParam(String firstTypeParam) {
this.firstTypeParam = firstTypeParam;
}
@Override
public String toString() {
return "firstTypeParam:" + firstTypeParam;
}
}
SecondPower class
public class SecondPower implements Power {
boolean anotherParam;
boolean isAnotherParam() {
return anotherParam;
}
void setAnotherParam(boolean anotherParam) {
this.anotherParam = anotherParam;
}
@Override
public String toString() {
return "anotherParam:" + String.valueOf(anotherParam);
}
}
Upvotes: 1