先看一下以下代码了解问题
ObjectMapper objectMapper = new ObjectMapper()
.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
// Map<Integer, Integer> map1 = new HashMap<>();
// map1.put(1, 1);
Map<Integer, Integer> map1 = ImmutableMap.of(1, 1);
// 使用 HashMap 时,content 是`["java.util.HashMap",{"1":1}]`;而使用 ImmutableMap 时是`{"1":1}`
String content = objectMapper.writeValueAsString(map1);
// 因为使用 ImmutableMap 时没有了类型信息,反序列化会报错。
// com.fasterxml.jackson.databind.exc.MismatchedInputException: Unexpected token (START_OBJECT), expected START_ARRAY: need JSON Array to contain As.WRAPPER_ARRAY type information for class java.util.Map
at [Source: (String)"{"1":1}"; line: 1, column: 1]
Map<Integer, Integer> map2 = objectMapper.readValue(content, new TypeReference<Map<Integer, Integer>>() {
});
按照JacksonPolymorphicDeserialization · FasterXML/jackson-docs Wiki所描述的,如果我没有理解错的话,使用 ImmutableMap 时没有保存类型信息是因为ImmutableMap.of(1, 1)
会产生SingletonImmutableBiMap
,而SingletonImmutableBiMap
是 final 的。
但是四种ObjectMapper.DefaultTyping
( JAVA_LANG_OBJECT, OBJECT_AND_NON_CONCRETE, NON_CONCRETE_AND_ARRAYS, NON_FINAL )都无法实现“保存SingletonImmutableBiMap
这个类型信息”。而ObjectMapper.setDefaultTyping(...)
也是依赖于ObjectMapper.DefaultTyping
的,所以也不行。
问题:Jackson 能够保存 final 类型的类型信息吗?如果可以的话,应该怎么做呢?
1
6IbA2bj5ip3tK49j 2020-07-10 13:19:13 +08:00
试过 activateDefaultTypingAsProperty 吗?
|
2
6IbA2bj5ip3tK49j 2020-07-10 13:35:18 +08:00
改成 ObjectMapper.DefaultTyping.EVERYTHING,就能保存了。
["com.google.common.collect.SingletonImmutableBiMap",{"1":1}] 但是没办法反序列化,因为这玩意儿没有构造器。 |
3
JasonLaw OP @xgfan #2 谢谢提示🙏
改变后的代码如下: PolymorphicTypeValidator ptv = BasicPolymorphicTypeValidator.builder() .allowIfSubType(ImmutableMap.class) .build(); ObjectMapper objectMapper = new ObjectMapper() .activateDefaultTyping(ptv, ObjectMapper.DefaultTyping.EVERYTHING); // Map<Integer, Integer> map1 = new HashMap<>(); // map1.put(1, 1); Map<Integer, Integer> map1 = ImmutableMap.of(1, 1); // content 为`["com.google.common.collect.SingletonImmutableBiMap",{"1":1}]` String content = objectMapper.writeValueAsString(map1); // 但是因为 SingletonImmutableBiMap 没有默认的构造器,反序列化报错 // com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot construct instance of `com.google.common.collect.SingletonImmutableBiMap` (no Creators, like default constructor, exist): no default constructor found at [Source: (String)"["com.google.common.collect.SingletonImmutableBiMap",{"1":1}]"; line: 1, column: 54] Map<Integer, Integer> map2 = objectMapper.readValue(content, new TypeReference<Map<Integer, Integer>>() { }); |
4
JasonLaw OP 我现在的处理方式是使用 HashMap 作为“中间人”来实现 ImmutableMap 的序列化和反序列化。参考 https://stackoverflow.com/a/34115875/5232255
|
5
azygote 2020-07-10 15:50:35 +08:00 via iPhone 1
你需要这个包 https://github.com/FasterXML/jackson-datatypes-collections 来进行 Guava 里面的一些 Collections 类的序列 /反序列化
|
6
JasonLaw OP @azygote #5 谢谢🙏
最后的代码为: PolymorphicTypeValidator ptv = BasicPolymorphicTypeValidator.builder() .allowIfSubType(ImmutableMap.class) .build(); ObjectMapper objectMapper = JsonMapper.builder() .addModule(new GuavaModule()) .activateDefaultTyping(ptv, ObjectMapper.DefaultTyping.EVERYTHING) .build(); Map<Integer, Integer> map1 = ImmutableMap.of(1, 1); // content 为`["com.google.common.collect.SingletonImmutableBiMap",{"1":1}]` String content = objectMapper.writeValueAsString(map1); // 能够成功反序列化 Map<Integer, Integer> map2 = objectMapper.readValue(content, new TypeReference<Map<Integer, Integer>>() { }); |