如何使用Jackson反序列化以带@JsonIdentityInfo的MyType为键的Map?
如何反序列化以MyType的id为键的Map JSON?
给定如下代码:
@JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "id") public class MyType { String id; ... // id的getter和setter String longText; } public class MyData { List<MyType> types; @JsonIdentityReference(alwaysAsId = true) // 仅序列化有效 Map<MyType, Integer> countMap; }
该代码可以将MyData正确序列化为以下JSON:
{ "types": [ { "id": "123", "longText": "very long text 1 ..." }, { "id": "456", "longText": "very long text 2 ..." } ], "countMap": { "123": 100, "456": 200 } }
但反序列化时会抛出错误:
com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot find a (Map) Key deserializer for type [simple type, class MyType]
解决方法
问题根源在于@JsonIdentityReference(alwaysAsId = true)仅处理序列化逻辑,将MyType对象替换为id字符串;但反序列化时,Jackson不知道如何把字符串键转换为MyType实例,因此需要自定义键反序列化器。
方法1:自定义KeyDeserializer并绑定到字段
- 实现
KeyDeserializer,定义字符串id到MyType的转换逻辑:
public class MyTypeKeyDeserializer extends KeyDeserializer { @Override public Object deserializeKey(String key, DeserializationContext ctxt) throws IOException { // 基础逻辑:直接通过id创建MyType实例 MyType myType = new MyType(); myType.setId(key); // 进阶逻辑:如果需要复用types列表中的已有实例(避免创建新对象) // 可以通过上下文获取当前正在反序列化的MyData对象,从types中匹配id // MyData myData = (MyData) ctxt.getParser().getParsingContext().getParent().getCurrentValue(); // for (MyType type : myData.getTypes()) { // if (key.equals(type.getId())) { // return type; // } // } return myType; } }
- 在
countMap字段上添加@JsonKeyDeserialize注解,指定使用自定义的反序列化器:
public class MyData { List<MyType> types; @JsonIdentityReference(alwaysAsId = true) @JsonKeyDeserialize(using = MyTypeKeyDeserializer.class) Map<MyType, Integer> countMap; }
方法2:全局注册KeyDeserializer
如果多个地方都需要处理MyType作为Map键的反序列化,可以在ObjectMapper中全局注册:
ObjectMapper objectMapper = new ObjectMapper(); SimpleModule module = new SimpleModule(); module.addKeyDeserializer(MyType.class, new MyTypeKeyDeserializer()); objectMapper.registerModule(module);
这样无需在每个字段上添加注解,全局生效。
内容的提问来源于stack exchange,提问作者Geoffrey De Smet
相关产品推荐
相关产品推荐

