1

使用jackson-module-Scala,我尝试使用Long as key序列化和反序列化具有内部 Map 的对象,但是 Jackson 将 key 序列化为 String 并且如果忽略 Class 中定义的类型,则不会将其反序列化为 Long 。是BUG吗?难道我做错了什么?

import com.fasterxml.jackson.databind.ObjectMapper
import com.fasterxml.jackson.module.scala.DefaultScalaModule
import com.fasterxml.jackson.module.scala.experimental.ScalaObjectMapper

case class InnerMap(map: Map[Long, Long])

object CrazyJackson {

  def main(args: Array[String]): Unit = {
    val mapper = new ObjectMapper() with ScalaObjectMapper
    mapper.registerModule(DefaultScalaModule)

    val innerMap = InnerMap(Map(1L->1L))
    val serialized = mapper.writeValueAsString(innerMap)
    val newObj = mapper.readValue(serialized, classOf[InnerMap])
    println(serialized) // Why the key is serialized as a String?
    println(innerMap)
    println(newObj)
    assert(newObj == innerMap)
  }

}

断言失败并且 println(serialized) 语句的输出是:

{"map":{"1":1}}

奇怪的是打印newObj和innerMap是一样的:

InnerMap(Map(1 -> 1))
InnerMap(Map(1 -> 1))

正如@Varren 所说,问题确实出在断言中。但:

import com.fasterxml.jackson.databind.ObjectMapper
import com.fasterxml.jackson.module.scala.DefaultScalaModule
import com.fasterxml.jackson.module.scala.experimental.ScalaObjectMapper
import org.scalatest.FunSuite

class CrazyJacksonTest extends FunSuite {
  test("test json comparision") {
    val mapper = new ObjectMapper() with ScalaObjectMapper
    mapper.registerModule(DefaultScalaModule)

    val innerMap = InnerMap(Map(1L->1L))
    val serialized = mapper.writeValueAsString(innerMap)
    val newObj = mapper.readValue(serialized, classOf[InnerMap])
    assert(newObj.map == innerMap.map)
  }
}

断言结果:

Map("1" -> 1) did not equal Map(1 -> 1)
ScalaTestFailureLocation: CrazyJacksonTest$$anonfun$1 at (CrazyJacksonTest.scala:17)
Expected :Map(1 -> 1)
Actual   :Map("1" -> 1)

我搞不清楚了!地图必须是 Map[Long,Long]

由于 Spark 依赖项,我必须使用此版本:

  • 斯卡拉 2.11.11
  • jackson-module-scala 2.6.5 并使用 2.9.1 版本进行测试,结果相同。

其他信息:

4

2 回答 2

1

JSON 只允许键名是字符串。ECMA-404 JSON 数据交换标准

对象结构表示为一对围绕零个或多个名称/值对的花括号标记。名称是一个字符串。

你是对的,断言问题来自杰克逊。在此处输入图像描述 如您所见classOf[InnerMap],实际上映射到Map<Object, Object>内部InnerMap,但您必须将此映射的 typeinfo 提交给 jackson 才能正确反序列化它。它在本文档中进行了解释,根据它您可以使用

case class InnerMap(@JsonDeserialize(keyAs = classOf[java.lang.Long])
                    map: Map[Long, Long])
于 2017-10-05T18:05:58.927 回答
-1

Scala Jackson 模块不会推断 Map 中键的类型。正如@Varren 回应的那样,解决方案是用杰克逊注释来注释模型,但是这样:

  • 该模型与特定的解析器相关联(模型定义中的杰克逊注释)。
  • 代码不太清楚。

所以我决定从 Jackson 搬到Circe删除注释以保持代码干净。这是一个证明它正确解析和反解析的测试:

  test("test json circe comparision") {

    import io.circe._
    import io.circe.generic.auto._
    import io.circe.parser._
    import io.circe.syntax._

    val innerMap = InnerMap(Map(1L -> 1L))

    val jsonStr = innerMap.asJson.noSpaces
    decode[InnerMap](jsonStr) match {
      case Right(innerMap2) => assert(innerMap2 == innerMap)
      case Left(error) => fail(error)
    }

  }

这并不意味着这是每个人的最佳解决方案。 Circe 有一个插件可以将它与 Jackson 解析器结合使用,但我没有测试它。

于 2017-10-13T14:54:16.620 回答