【问题标题】:Deserializing non-string map keys with Jackson使用 Jackson 反序列化非字符串映射键
【发布时间】:2012-06-28 14:21:36
【问题描述】:

我有一张像这样的地图:

public class VerbResult {
    @JsonProperty("similarVerbs")
    private Map<Verb, List<Verb>> similarVerbs;
}

我的动词类如下所示:

public class Verb extends Word {
    @JsonCreator
    public Verb(@JsonProperty("start") int start, @JsonProperty("length") int length,
            @JsonProperty("type") String type, @JsonProperty("value") VerbInfo value) {
        super(length, length, type, value);
    }
    //...
}

我想序列化和反序列化 VerbResult 类的实例,但是当我这样做时出现此错误:Can not find a (Map) Key deserializer for type [simple type, class my.package.Verb]

我在网上看到您需要告诉 Jackson 如何反序列化映射键,但我没有找到任何说明如何执行此操作的信息。动词类也需要在地图之外进行序列化和反序列化,因此任何解决方案都应保留此功能。

感谢您的帮助。

【问题讨论】:

  • 你读过stackoverflow.com/questions/6371092/… 吗?他的情况乍一看和你的很相似。
  • 是的,我已经读过了,但实际上我并没有在那里找到问题的答案。如何使用模块来解决这个问题?
  • Verb 是 POJO 吗?

标签: java jackson


【解决方案1】:

经过一天的搜索,我发现了一种基于this question 的更简单的方法。解决方案是将@JsonDeserialize(keyUsing = YourCustomDeserializer.class) 注释添加到地图中。然后通过扩展KeyDeserializer 并覆盖deserializeKey 方法来实现您的自定义反序列化器。该方法将使用字符串键调用,您可以使用该字符串来构建真实对象,甚至从数据库中获取现有对象。

所以首先在地图声明中:

@JsonDeserialize(keyUsing = MyCustomDeserializer.class)
private Map<Verb, List<Verb>> similarVerbs;

然后创建将使用字符串键调用的反序列化器。

public class MyCustomDeserializer extends KeyDeserializer {
    @Override
    public MyMapKey deserializeKey(String key, DeserializationContext ctxt) throws IOException, JsonProcessingException {
        //Use the string key here to return a real map key object
        return mapKey;
    }
}

适用于 Jersey 和 Jackson 2.x

【讨论】:

  • 我一直试图将注释放在键类本身(在您的情况下为 Verb)而不是地图声明上。你帮我省了很多麻烦,谢谢!
  • 对于示例解串器,请参见此处baeldung.com/jackson-map
  • 但是这里如何使用字符串键返回一个真正的映射键对象呢?
【解决方案2】:

如上所述,诀窍是您需要一个 key 反序列化器(这也让我感到困惑)。在我的例子中,在我的类上配置了一个非字符串映射键,但它不在我解析的 JSON 中,所以一个非常简单的解决方案对我有用(只需在键解串器中返回 null)。

public class ExampleClassKeyDeserializer extends KeyDeserializer
{
    @Override
    public Object deserializeKey( final String key,
                                  final DeserializationContext ctxt )
       throws IOException, JsonProcessingException
    {
        return null;
    }
}

public class ExampleJacksonModule extends SimpleModule
{
    public ExampleJacksonModule()
    {
        addKeyDeserializer(
            ExampleClass.class,
            new ExampleClassKeyDeserializer() );
    }
}

final ObjectMapper mapper = new ObjectMapper();
mapper.registerModule( new ExampleJacksonModule() );

【讨论】:

    【解决方案3】:

    answer given here 为基础,建议使用反序列化器实现模块。 JodaTime Module 是一个易于理解的包含序列化器和反序列化器的模块的完整示例。

    请注意,模块功能是在 Jackson 1.7 版中引入的,因此您可能需要升级。

    所以一步一步来:

    1. 根据 Joda 示例为您的类创建一个包含(反)序列化程序的模块
    2. 将该模块注册到mapper.registerModule(module);

    一切就绪

    【讨论】:

    • 另外一个注意事项:您需要添加的是“key (de)serializers”:常规(de)serializers 不能按原样使用,因为 Map 键有额外的限制。但是注册肯定可以从模块中完成。
    • @Max 您能否提供有关如何实现反序列化器的代码?如果可能的话,我想看看 =)
    【解决方案4】:

    在网上搜索之后,我认为我对如何处理 POJO 样式的键有一个不错的解决方案(尽管一如既往,最好不要使用完整的对象作为映射键)。

    序列化器(注册为 Jackson 模块,在 Spring Boot 内部):

    @Bean
    fun addKeySerializer(): Module =
        SimpleModule().addKeySerializer(YourClass::class.java, YourClassSerializer())
    
    class YourClassSerializer() : JsonSerializer<YourClass>() {
        override fun serialize(value: DataElement, gen: JsonGenerator, serializers: SerializerProvider) {
            gen.writeFieldName(jacksonObjectMapper().writeValueAsString(value))
        }
    }
    

    (请注意,在标准 Java 环境中,您必须在此处实例化自己的 objectMapper 实例)

    反序列化器:

    @Bean
    fun addKeyDeserializer(): Module =
        SimpleModule().addKeyDeserializer(YourClass::class.java, YourClassDeserializer())
    
    class YourClassDeserializer() : KeyDeserializer() {
        override fun deserializeKey(key: String, ctxt: DeserializationContext): YourClass? {
            return ctxt.parser.readValueAs(YourClass::class.java)
        }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-10-31
      • 1970-01-01
      • 2019-01-20
      • 2016-08-11
      • 2015-10-25
      • 1970-01-01
      相关资源
      最近更新 更多