简体   繁体   English

jackson-module-scala将Map中的长键序列化/反序列化为字符串

[英]jackson-module-scala serialize/deserialize Long keys in Map as Strings

Using jackson-module-Scala, I try to serialize and deserialize an object with an inner Map using a Long as key , but the Jackson serializes the key as String and doesn't deserialize it as Long ifgnoring the type efined in the Class. 使用jackson-module-Scala,我尝试使用Long as键将带有内部Map的对象序列化和反序列化,但是Jackson忽略了在类中定义的类型时,将键序列化为String,并且不将其反序列化为Long。 Is it a BUG? 是BUG吗? Am I doing something wrong? 难道我做错了什么?

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)
  }

}

The assert fails and the output of the println(serialized) statement is : 断言失败,println(serialized)语句的输出为:

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

It is strange that printing newObj and innerMap is the same: 奇怪的是,打印newObj和innerMap是相同的:

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

As @Varren says, the problem really is in the assert. 正如@Varren所说,问题确实出在断言中。 But: 但:

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)
  }
}

The assert result: 断言结果:

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)

I am lost! 我搞不清楚了! The map must be a Map[Long,Long] ! 该地图必须是Map [Long,Long]

I must use this version because of Spark dependencies: 由于Spark依赖关系,我必须使用此版本:

  • Scala 2.11.11 斯卡拉2.11.11
  • jackson-module-scala 2.6.5 and also test with version 2.9.1 with the same result. jackson-module-scala 2.6.5,并使用2.9.1版本进行测试,结果相同。

Other info: 其他资讯:

JSON allows key names to be strings only. JSON允许键名只能是字符串。 ECMA-404 The JSON Data Interchange Standard ECMA-404 JSON数据交换标准

An object structure is represented as a pair of curly bracket tokens surrounding zero or more name/value pairs. 对象结构被表示为一对大括号标记,它们围绕零个或多个名称/值对。 A name is a string. 名称是一个字符串。

You are right and the assertion problem comes from Jackson. 您说得对,断言问题来自杰克逊。 在此处输入图片说明 As you can see classOf[InnerMap] actually maps to Map<Object, Object> inside InnerMap but you have to submit typeinfo of this map to jackson to deserialize it correctly. 正如你所看到classOf[InnerMap]实际上映射到Map<Object, Object>里面InnerMap但你必须提交所属类别此地图杰克逊的正确反序列化。 It is explained in this documentation and according to it you can just use 本文档对此进行了说明 ,根据该说明 ,您可以使用

case class InnerMap(@JsonDeserialize(keyAs = classOf[java.lang.Long])
                    map: Map[Long, Long])

The Scala Jackson module does not infer the type of the key in the Map. Scala Jackson模块无法推断Map中密钥的类型。 As @Varren responded, the solution is to annotate the model with Jackson annotations, but on this way: 正如@Varren回答的那样,解决方案是使用Jackson注释对模型进行注释,但是采用这种方式:

  • The model is tied to a specific parser (Jackson annotations in the model definition). 该模型绑定到特定的解析器(模型定义中的Jackson批注)。
  • The code is less clear. 代码不太清楚。

So I decided to move from Jackson to Circe and remove annotations to keep the code clean. 因此,我决定从Jackson转移到Circe,删除注释以保持代码干净。 This is a test that proves that it is parsing and unparsing correctly: 此测试证明它在正确解析和未解析:

  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)
    }

  }

It is not mean that this is the best solution for everybody. 这并不意味着这是每个人的最佳解决方案。 Circe has a plugin to use it in combination with Jackson parser, but I did not test it. Circe有一个插件可以与Jackson解析器结合使用,但是我没有对其进行测试。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM