如何使用Jackson反序列化Map<?, ?>?

5
我正在尝试使用Jackson 2.8将带有任意对象作为键的Map<?, ?>进行序列化/反序列化。JSON对应应该是一组夫妇,即给定:
public class Foo {
    public String foo;
    public Foo(String foo) {
        this.foo = foo;
    }
}

public class Bar {
    public String bar;
    public Bar(String bar) {
        this.bar = bar;
    }
}

那么

Map<Foo, Bar> map;
map.put(new Foo("foo1"), new Bar("bar1"));
map.put(new Foo("foo2"), new Bar("bar2"));

应该用这个JSON来表示。
[
    [ { "foo": "foo1" }, { "bar": "bar1" } ],
    [ { "foo": "foo2" }, { "bar": "bar2" } ]
]

所以我将序列化部分做成了

public class MapToArraySerializer extends JsonSerializer<Map<?, ?>> {

    @Override
    public void serialize(Map<?, ?> value, JsonGenerator gen, SerializerProvider serializers)
        throws IOException, JsonProcessingException {
        gen.writeStartArray();
        for (Map.Entry<?, ?> entry : value.entrySet()) {
            gen.writeStartArray();
            gen.writeObject(entry.getKey());
            gen.writeObject(entry.getValue());
            gen.writeEndArray();
        }
        gen.writeEndArray();
    }

}

但我不知道如何编写JsonDeserializer来执行反向操作。有任何建议吗?
注意:我需要使用[ [ "key1", "value1" ], [ "key2", "value2" ] ]表示法才能在JavaScript中消耗该JSON,new Map( ... )JSON.stringify(map) 也可以生成该表示法(请参见https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Map)。
为了澄清,这样的映射将是其他类的字段,例如:
public class Baz {

    @JsonSerialize(using = MapToArraySerializer.class)
    @JsonDeserialize(using = ArrayToMapDeserializer.class, keyAs = Foo.class, contentAs = Bar.class)
    Map<Foo, Bar> map;

}

我需要帮助的地方是ArrayToMapDeserializer extends JsonDeserializer<Map<?, ?>>,这与IT技术有关。


我认为你过于复杂化了。你的JSON不是一个映射。为什么要使用Map来表示它?FooBar有任何不同的属性吗? - cassiomolin
是的,它们可以是完全不同的对象(任何Java对象)。我已经更新了问题以澄清这一点! - Giovanni Lovato
他们能够实现相同的接口或者扩展相同的类吗? - cassiomolin
很遗憾,我正在寻找一种通用的方法来使用这个符号(de)serialize Java maps,例如,任何Java类都应该可以作为键类型。 - Giovanni Lovato
如果它们可以是任何Java对象,在反序列化JSON时,你如何确定要实例化的类型? - cassiomolin
显示剩余2条评论
2个回答

12

我想出了这个解决方案:

public class ArrayToMapDeserializer extends JsonDeserializer<SortedMap<Object, Object>>
    implements ContextualDeserializer {

    private Class<?> keyAs;

    private Class<?> contentAs;

    @Override
    public Map<Object, Object> deserialize(JsonParser p, DeserializationContext ctxt)
        throws IOException, JsonProcessingException {
        return this.deserialize(p, ctxt, new HashMap<>());
    }

    @Override
    public Map<Object, Object> deserialize(JsonParser p, DeserializationContext ctxt,
        Map<Object, Object> intoValue) throws IOException, JsonProcessingException {
        JsonNode node = p.readValueAsTree();
        ObjectCodec codec = p.getCodec();
        if (node.isArray()) {
            node.forEach(entry -> {
                try {
                    JsonNode keyNode = entry.get(0);
                    JsonNode valueNode = entry.get(1);
                    intoValue.put(keyNode.traverse(codec).readValueAs(this.keyAs),
                        valueNode.traverse(codec).readValueAs(this.contentAs));
                } catch (NullPointerException | IOException e) {
                    // skip entry
                }
            });
        }
        return intoValue;
    }

    @Override
    public JsonDeserializer<?> createContextual(DeserializationContext ctxt, BeanProperty property)
        throws JsonMappingException {
        JsonDeserialize jsonDeserialize = property.getAnnotation(JsonDeserialize.class);
        this.keyAs = jsonDeserialize.keyAs();
        this.contentAs = jsonDeserialize.contentAs();
        return this;
    }

}

可以这样使用:

public class Baz {

    @JsonSerialize(using = MapToArraySerializer.class)
    @JsonDeserialize(using = ArrayToMapDeserializer.class,
        keyAs = Foo.class, contentAs = Bar.class)
    Map<Foo, Bar> map;

}

0

这里是反序列化:

@Override
public Map<?, ?> deserialize(JsonParser p, DeserializationContext ctxt)
        throws IOException, JsonProcessingException {
    Map map = new LinkedHashMap();

    ObjectCodec oc = p.getCodec();
    JsonNode anode = oc.readTree(p);

    for (int i = 0; i < anode.size(); i++) {
        JsonNode node = anode.get(i);
        map.put(node.get(0), node.get(1));
    }

    return map;
}

我在原始解决方案中添加了一些测试用例,并使用新的 Oson 实现进行了转换,但使用不同的约定:将映射到 JSON 中的格式为 {key1: value1, key2: value2, ...},因此 JSON 输出如下:

{
  {
    "foo": "foo1"
  }: {
    "bar": "bar1"
  },
  {
    "foo": "foo2"
  }: {
    "bar": "bar2"
  }
}

你可以查看源代码


2
我要求使用Jackson和特定输出,但你提供了另一个工具和不同的输出。请查看此链接:http://stackoverflow.com/help/how-to-answer - Giovanni Lovato
没有正确的配置,jackson 得到了这个结果: {"ca.oson.json.listarraymap.Foo@5f5a92bb":{"bar":"bar1"},"ca.oson.json.listarraymap.Foo@6fdb1f78":{"bar":"bar2"}} - David He
ArrayToMapDeserializer 应该不难实现,等我晚上有时间了就为你试一下。 - David He
感谢您的努力,但是您的答案再次没有遵守问题所需的规格。请查看其他答案以获取可行的解决方案! - Giovanni Lovato

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接