2017-10-05 168 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解析器結合使用,但我沒有對它進行測試。