将JSON对象/贴图反序列化为与Jackson的通用集合

时间:2012-03-29 02:16:03

标签: json jackson

我有这样的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仍然必须有一个no-arg构造函数,所以我最初构造一个破坏的对象:

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 :(得分:-1)

看起来你停止使用杰克逊,但对于有类似问题的人,你可以打开DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY。启用该设置后,当杰克逊在JSON中找到一个对象但是应该反序列化为一个集合时,它将创建一个集合并将该对象放入集合中,这看起来就像你想要的那样。