Я хочу десериализовать полезную нагрузку JSON, представляющую поле, которое может быть строкой (нерасширенной) или объектом (расширенной):
Пример полезной нагрузки unexpanded: отправляется только строковое значение, которое содержит идентификатор.
{
"id" : "777424881071",
"category" : "/category/12",
"title" : "ACADEMY DINOSAUR",
"description" : "A Epic Drama of ...",
}
Пример полезной нагрузки expanded: отправляется соответствующий идентификатор:
{
"id" : "777424881071",
"category" : {
"id" : "12",
"name" : "Children"
},
"title" : "ACADEMY DINOSAUR",
"description" : "A Epic Drama of ...",
}
В java это может быть представлено таким типом:
public class ExpandableField<T> {
private String id;
private T expandedObject;
public ExpandableField(String id, T expandedObject) {
this.id = id;
this.expandedObject = expandedObject;
}
}
См. ExpandableField для полного примера из полосатого java-клиента.
Мне интересно, как я могу написать JsonDeserializer<ExpandableField<?>>
для Джексона.
public class ExpandableFieldDeserializer extends JsonDeserializer<ExpandableField<?>> {
@Override
public ExpandableField<?> deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JsonProcessingException {
JsonToken token = p.getCurrentToken();
if (token == JsonToken.VALUE_STRING) {
return new ExpandableField<>(p.getValueAsString(), null);
}
else if (token == JsonToken.START_OBJECT) {
//TODO deserialize object and compute id
return new ExpandableField<>(id, object);
}
return null;
}
}
Мое текущее решение состоит в том, чтобы загрузить текущее дерево и жестко указать соответствующий класс с помощью небольшого метода: private Class<?> computeType(JsonNode node)
.
public class ExpandableFieldDeserializer extends JsonDeserializer<ExpandableField<?>> {
@Override
public ExpandableField<?> deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JsonProcessingException {
JsonToken token = p.getCurrentToken();
if (token == JsonToken.VALUE_STRING) {
return new ExpandableField<>(p.getValueAsString(), null);
}
else if (token == JsonToken.START_OBJECT) {
ObjectCodec codec = p.getCodec();
JsonNode node = codec.readTree(p);
String id = null;
if (node.has("id")) {
id = node.asText();
}
Class<?> typeClass = computeType(node);
Object object = codec.treeToValue(node, typeClass);
return new ExpandableField<>(id, object);
}
return null;
}
private Class<?> computeType(JsonNode node) {
//...
}
}
Я бы предпочел что-то более динамичное.
Для сравнения, в библиотеке gson тип доступен в сигнатуре метода:
deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
с ((ParameterizedType) typeOfT).getActualTypeArguments()[0]
context
может десериализовать объект с context.deserialize(json, t)
.
См. полный пример здесь: Десериализатор расширяемого поля
Как можно сделать что-то подобное с Джексоном?