jackson-module-scala序列化/反序列化以字符串形式映射Long键
问题描述:
使用jackson-module-Scala,我尝试使用作为关键字对内部Map进行序列化和反序列化,但Jackson将关键字序列化作为字符串,并且不会将其反序列化为Long,如果忽略类中定义的类型。 这是一个错误吗?难道我做错了什么?jackson-module-scala序列化/反序列化以字符串形式映射Long键
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的输出(连载)语句是:
{"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)
我迷路了! 该地图必须是地图[Long,Long]!
我必须使用这个版本,因为星火依赖:
- 斯卡拉2.11.11
- 杰克逊模块 - 斯卡拉2.6.5,并与版本2.9.1具有相同的结果测试。
其他信息:
答
JSON允许键名称只能是字符串。 ECMA-404 The JSON Data Interchange Standard
一个目的结构被表示为一对大括号记号 的周边零个或多个名称/值对。 名称是一个字符串。
你是对的,而断言问题来自杰克逊。 正如你所看到的classOf[InnerMap]
实际上映射到Map<Object, Object>
里面InnerMap
但你必须提交这张地图的信息到杰克逊正确反序列化它。它在this documentation解释,并根据它,你可以只使用
case class InnerMap(@JsonDeserialize(keyAs = classOf[java.lang.Long])
map: Map[Long, Long])
答
斯卡拉杰克逊模块不推断结构图中的密钥类型。 作为@Varren回应,解决的办法是与注释的注释杰克逊模型,但在这种方式:
- 该模型依赖于特定的解析器(杰克逊注释在模型中的定义)。
- 代码不太清楚。
所以我决定从杰克逊移到Circe和删除注释保持代码的清洁。 这是证明它是分析和正确unparsing测试:
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解析器结合使用,但我没有对它进行测试。
所以问题与断言。我认为案例类实现了equals和hashCode。事实上,为什么“断言(InnerMap(Map(1L-> 1L))== InnerMap(Map(1L-> 1L)))”ok? – angelcervera
请你能检查我的新例子吗?我错过了一些东西。如果case类被定义为Map [Long,Long],断言如何可能将管理内部对象作为Map [String,Long] – angelcervera
指向“如何在Scala中比较对象以实现相等性”的链接?是一种不同的情况,因为是比较类而不是案例类。 – angelcervera