2017-10-05 134 views
1

使用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具有相同的结果测试。

其他信息:

回答

0

JSON允许键名称只能是字符串。 ECMA-404 The JSON Data Interchange Standard

一个目的结构被表示为一对大括号记号 的周边零个或多个名称/值对。 名称是一个字符串。

你是对的,而断言问题来自杰克逊。 enter image description here 正如你所看到的classOf[InnerMap]实际上映射到Map<Object, Object>里面InnerMap但你必须提交这张地图的信息到杰克逊正确反序列化它。它在this documentation解释,并根据它,你可以只使用

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

所以问题与断言。我认为案例类实现了equals和hashCode。事实上,为什么“断言(InnerMap(Map(1L-> 1L))== InnerMap(Map(1L-> 1L)))”ok? – angelcervera

+0

请你能检查我的新例子吗?我错过了一些东西。如果case类被定义为Map [Long,Long],断言如何可能将管理内部对象作为Map [String,Long] – angelcervera

+0

指向“如何在Scala中比较对象以实现相等性”的链接?是一种不同的情况,因为是比较类而不是案例类。 – angelcervera

0

斯卡拉杰克逊模块不推断结构图中的密钥类型。 作为@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解析器结合使用,但我没有对它进行测试。