Я хотел бы десериализовать строку json, которая может иметь «неизвестное» свойство (но известного универсального свойства во время компиляции), и десериализовать это неизвестное свойство в предоставленный универсальный тип.
Строка JSON структурно может выглядеть следующим образом:
{
"id":"abc",
"human": {"name": "me", age: 99}
}
OR:
{
"id":"abc",
"cat": {"color": "orange"}
}
OR:
{
"id":"abc",
"bike": {"specs": {"speed":30}}
}
У меня есть следующие классы моделей:
@Value
@Builder
@Jacksonized
public class Data<T> {
String id;
T other; // <- how can I map "cat", "human", or "bike" into this generic
}
@V/B/J
public class Human {
String name;
Integer age;
}
@V/B/J
public class Cat {
String color;
}
@V/B/J
public class Bike {
Specs specs;
}
@V/B/J
public class Specs {
Integer speed;
}
Требуемый универсальный вариант строки JSON будет известен во время компиляции, например:
Data<Human> humanData = objectMapper.readValue(jsonHumanString, Data.class);
Data<Cat> catData = objectMapper.readValue(jsonCatString, Data.class);
Data<Bike> bikeData = objectMapper.readValue(jsonBikeString, Data.class);
Однако я все еще получаю сообщение об ошибке: com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException: Unrecognized field "human"
. Как я могу принудительно сопоставить «велосипед», «кошку» и «человека» с «другим»?
Думаю, я нашел свой собственный ответ. Я могу использовать собственный десериализатор для всего класса Data
, который разрешает дженерики.
@Value
@Builder
@JsonDeserialize(using = GenericDeserializer.class)
public class Data<T> {
String id;
T other;
}
Десериализатор:
public class GenericDeserializer extends StdDeserializer<Data<?>> {
public GenericDeserializer() {
this(null);
}
public GenericDeserializer(Class<Data<?>> vc) {
super(vc);
}
@Override
public Data<?> deserialize(JsonParser jsonParser, DeserializationContext deserializationContext)
throws IOException, JsonProcessingException {
ObjectCodec codec = jsonParser.getCodec();
JsonNode node = codec.readTree(jsonParser);
if (node.has("bike")) {
return Data.<Bike>builder()
.id(String.valueOf(node.get("id")))
.other(codec.treeToValue(node.get("bike"), Bike.class))
.build();
} else if (node.has("human")) {
return Data.<Human>builder()
.id(String.valueOf(node.get("id")))
.other(codec.treeToValue(node.get("human"), Human.class))
.build();
} else if (node.has("cat")) {
return Data.<Cat>builder()
.id(String.valueOf(node.get("id")))
.other(codec.treeToValue(node.get("cat"), Cat.class))
.build();
}
}
}