使用jackson序列化Map.Entry时出现问题

时间:2017-08-06 17:49:47

标签: serialization junit jackson deserialization junit4

如果我尝试反序列化存储为String的下面类型:

 List<Entry<String, String>> entryList;

其中entryList包含:

[{"dummyKey1":"dummyValue1"}]

我收到以下错误

 Caused by: com.fasterxml.jackson.databind.JsonMappingException: Can not construct instance of java.util.Map$Entry, problem: abstract types either need to be mapped to concrete types, have custom deserializer, or be instantiated with additional type information.

我在junit中运行测试用例时遇到上述错误,但是如果我删除了测试用例,那么部署完成后运行正常:

由于Entry中缺少NoArgsConstructor,因此在运行junit测试用例时出现上述错误。因此,我使用NoArgsConstructor创建了一个DummyEntry,它将带有参数的Entry调用为null。

   DummyEntry<K, V> extends SimpleEntry<K, V>

进行此更改后,上面的错误没有出现,但是在部署更改后我开始低于错误。

  Caused by: com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException:
  Unrecognized field "dummyKey1", not marked as ignorable (2 known properties: "value", "key"]).

一种方法对junit不起作用的原因是什么,但在生产中它起作用,而其他方式在junit中起作用但不在生产中起作用。

另外,我注意到另外一件事:在制作中,Map.Entry被序列化为

  {'dummyKey1':'dummyValue1'}

然而,junit中的测试用例序列化与

相同的字符串
 {'key':'dummyKey1', 'value':'dummyValue1'}

这种奇怪行为的原因是什么?我怎样才能让这件事适合两者?

2 个答案:

答案 0 :(得分:1)

我怀疑您可能遇到Map.Entry的不同序列化策略的问题。

在jackson-databind Map.Entry was supported as a 'known type'的v2.5.0(IIRC)中。在此版本之前,Map.Entry的keyvalue属性将出现在序列化的Map.Entry中。在此版本之后,情况已不再如此。

以下是一些示例测试用例,显示了我的意思:

@Test
public void mapSerialisationPreJackson2_5_0() throws IOException {
  Map<String, String> aMap = Maps.newHashMap();
  aMap.put("dummyKey1", "dummyValue1");

  Set<Map.Entry<String, String>> incoming = aMap.entrySet();

  ObjectMapper objectMapper = new ObjectMapper();

  String serialised = objectMapper.writeValueAsString(incoming);

  // prints: [{"key":"dummyKey1","value":"dummyValue1"}]
  System.out.println(serialised);

  Set<Map.Entry<String, String>> deserialised = objectMapper.readValue(serialised, Set.class);

  // prints: [{key=dummyKey1, value=dummyValue1} (just like you posted in your question) whereas for versions > 2.5.0 the serialised form is ]
  System.out.println(deserialised);
}

@Test
public void mapSerialisationPostJackson2_5_0() throws IOException {
  Map<String, String> aMap = Maps.newHashMap();
  aMap.put("dummyKey1", "dummyValue1");

  Set<Map.Entry<String, String>> incoming = aMap.entrySet();

  ObjectMapper objectMapper = new ObjectMapper();

  String serialised = objectMapper.writeValueAsString(incoming);

  // prints: [{"dummyKey1":"dummyValue1"}]
  System.out.println(serialised);

  Set<Map.Entry<String, String>> deserialised = objectMapper.readValue(serialised, Set.class);

  // prints: [{dummyKey1=dummyValue1}]
  System.out.println(deserialised);
}

在v2.5.0之前,Map.Entry将序列化为{key=dummyKey1, value=dummyValue1}(就像您在问题中发布的那样),而对于版本&gt; 2.5.0序列化表格为{dummyKey1=dummyValue1}

我认为您在测试环境中使用的是jackson-databind版本,其中&lt; 2.5.0和生产环境中的jackson-databind版本是&gt; 2.5.0

答案 1 :(得分:0)

为了能够将[{"dummyKey1":"dummyValue1"}]反序列化为List<Entry<String, String>>变量,您可以:

  1. 使用Jackson的参数名称模块。阅读更多here。它基本上允许带有参数的非注释的非默认构造函数用于反序列化类。在这种情况下,Map.Entry的各种实现的构造函数。如果你使用Java 8,这是一个非常直接的解决方案。
  2. 如果您不能使用参数名称模块(例如Java 7),您可以考虑使用mixins来注释类的构造函数,而无需修改它的源代码。我抓紧了,这很棘手。例如,对于HashMapMap.Entry的实施是Node,其具有包私有可见性。