使用Jackson从JSON反序列化Scala映射字段

时间:2018-08-13 15:55:20

标签: json scala jackson

我正在尝试使用Jackson ObjectMapper将Scala类序列化/反序列化为JSON。序列化工作正常,但是我遇到了试图重新读回JSON的类型异常。我通过添加适当的批注来修复了大多数异常,但是它不适用于我的Map成员...似乎杰克逊正在尝试处理密钥JSON对象中作为类中的属性,而不是映射中的键。 (我相信这与其他类似问题不同,因为他们直接在地图内容上调用了readValue。)

这是我的ObjectMapper设置:

val mapper = new ObjectMapper() with ScalaObjectMapper
mapper.registerModule(DefaultScalaModule)

这是我带注释的类和成员的样子:

@JsonInclude(JsonInclude.Include.NON_DEFAULT)
class MyClass extends Serializable {
  @JsonDeserialize(
    as = classOf[mutable.HashMap[String, Long]],
    keyAs = classOf[java.lang.String],
    contentAs = classOf[java.lang.Long]
  )
  val counts = mutable.Map.empty[String, Long]
}

如果我给它一些JSON,如:

{"counts":{"foo":1,"bar":2}}

并用mapper.readValue[MyClass](jsonString)

阅读

我遇到像UnrecognizedPropertyException: Unrecognized field "foo" (class mutable.HashMap), not marked as ignorable这样的异常。

我尝试将DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES添加到我的映射器配置中,但是在这种情况下似乎没有任何作用,而且我不确定是否需要这种全局设置。

我如何说服Jackson将字符串“ foo”和“ bar”当作地图成员字段中的键而不是HashMap类中的属性?自动写出来似乎做对了。

还值得注意的是:在对临时文件或字符串变量进行快速出入单元测试中,反序列化似乎可以正常工作,但是当我尝试运行整个应用程序并读取其先前编写的JSON时,反序列化似乎效果很好。据我所知,它正在进行相同的readValue调用,我不知道为什么它在测试中似乎可以正常工作。

3 个答案:

答案 0 :(得分:0)

我做了一个简单的测试,像这样:

case class TestClass (counts: mutable.HashMap[String, Long])

我将其转换为:

val objectMapper = new ObjectMapper() with ScalaObjectMapper
objectMapper.registerModule(DefaultScalaModule)

val r3 = objectMapper.readValue("{\"counts\":{\"foo\":1,\"bar\":2}}", classOf[TestClass])

显然,它对我有用。可能与您使用的Jackson或Scala版本有关。例如,您尝试过其他版本的Jackson吗?

答案 1 :(得分:0)

我的问题是由于没有在我的mapper配置单例中使用链接而导致的竞争状况。

我的旧代码更像这样:

private var mapper: ObjectMapper with ScalaObjectMapper = _

def getMapper: ObjectMapper with ScalaObjectMapper = {
  if (mapper == null) {
    mapper = new ObjectMapper() with ScalaObjectMapper
    mapper.registerModule(DefaultScalaModule)
    mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES)
  }
  mapper
}

如您所见,如果一个线程初始化了映射器,但尚未禁用未知属性失败,则另一个线程可能返回并使用尚未设置该标志的映射器,这解释了为什么我看到了仅在某些时间出现错误。

正确的代码使用链接,以便使用所有配置来设置映射器单例:

private var mapper: ObjectMapper = _

 def getMapper: ObjectMapper = {
   if (mapper == null) {
     mapper = new ObjectMapper()
       .registerModule(DefaultScalaModule)
       .disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES)
   }
   mapper
 }

(我还删除了实验性ScalaObjectMapper混入。)

答案 2 :(得分:-1)

尝试jsoniter-scala,您将享受到这些天来如何使用Scala轻松,安全,高效地解析和序列化JSON:https://github.com/plokhotnyuk/jsoniter-scala

它的关键功能之一是能够在编译时生成编解码器,甚至可以打印其源代码。您将不会遇到任何会影响代码的运行时魔术,例如反射或字节代码替换。