Десериализовать объект / карту JSON как универсальную коллекцию с Джексоном - PullRequest
4 голосов
/ 29 марта 2012

У меня есть такие карты 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;
    }
}

Есть ли лучший / более простой способ сделать это?

1 Ответ

0 голосов
/ 13 января 2013

Похоже, вы перестали использовать Джексона, но для тех, у кого есть похожие проблемы, вы можете включить DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY. Если этот параметр включен, Джексон найдет объект в JSON, но должен десериализовать в коллекцию, он создаст коллекцию и поместит объект в коллекцию, которая выглядит здесь так, как вам нужно.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...