У меня есть такие карты JSON:
"things": {"foo": {"name": "foo", ...}, "bar": {"name": "bar", ...}}
Я хочу десериализовать их, как если бы они были массивами:
"things": [{"name": "foo", ...}, {"name": "bar", ...}]
(для соответствия поведению десериализации XML / JAXB):
<things><thing name="foo">...</thing><thing name="bar">...</thing></things>
в коллекцию, такую как эта:
@XmlElementWrapper
@XmlElement(name = "thing")
@JsonDeserialize(using = MapToCollectionDeserializer.class)
Collection<Thing> things;
Обратите внимание, что у меня есть коллекции с различными типами элементов - не просто Thing
- поэтому мне нужен универсальный механизм.
Однако, когда вы пишете пользовательский десериализатор, как правильно получить доступ к информации о типе контекста?
public class MapToCollectionDeserializer extends StdDeserializer<Object>
{
@Override
public Object deserialize(JsonParser jp, DeserializationContext ctxt)
throws IOException, JsonProcessingException
{
Preconditions.checkState(jp.getCurrentToken() == JsonToken.START_OBJECT);
final LinkedList<Object> result = new LinkedList<>();
JsonToken tok;
while ((tok = jp.nextToken()) != JsonToken.END_OBJECT)
{
Preconditions.checkState(tok == JsonToken.FIELD_NAME);
// How to get the collection element type for deserialization?
result.add(...);
}
return result;
}
}
Мой подход до сих пор использует ContextualDeserializer
, который может обеспечить BeanProperty
(который содержит информацию о типе) для десериализатора.Тем не менее, JsonDeserializer должен по-прежнему иметь конструктор без аргументов, поэтому я вначале создаю сломанный объект:
public class MapToCollectionDeserializer extends StdDeserializer<Object>
implements ContextualDeserializer<Object>
{
private final BeanProperty property;
public MapToCollectionDeserializer()
{
super(Collection.class);
property = null; // YUCK: BROKEN!!!
}
private MapToCollectionDeserializer(BeanProperty property)
{
super(property.getType());
this.property = property;
}
@Override
public JsonDeserializer<Object> createContextual(DeserializationConfig config,
BeanProperty property) throws JsonMappingException
{
return new MapToCollectionDeserializer(property);
}
@Override
public Object deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException,
JsonProcessingException
{
Preconditions.checkState(jp.getCurrentToken() == JsonToken.START_OBJECT);
final JavaType elementType = property.getType().containedType(0);
final LinkedList<Object> result = new LinkedList<>();
JsonToken tok;
while ((tok = jp.nextToken()) != JsonToken.END_OBJECT)
{
Preconditions.checkState(tok == JsonToken.FIELD_NAME);
jp.nextToken();
final JsonDeserializer<Object> valueDeser = ctxt.getDeserializerProvider()
.findValueDeserializer(ctxt.getConfig(), elementType, property);
result.add(valueDeser.deserialize(jp, ctxt));
}
return result;
}
}
Есть ли лучший / более простой способ сделать это?