不可变映射(反)序列化 to/from 播放 JSON

immutable Map (de)serialization to/from Play JSON

我有以下(简化的)结构:

case class MyKey(key: String)
case class MyValue(value: String)

让我们假设我有 Play JSON 两种情况的格式化程序 类。

例如我有:

val myNewMessage = collection.immutable.Map(MyKey("key1") -> MyValue("value1"), MyKey("key2") -> MyValue("value2"))

由于以下转换

play.api.libs.json.Json.toJson(myNewMessage)

我期待的是:

{ "key1": "value1", "key2": "value2" }

我试过编写格式化程序,但不知为何我无法成功:

implicit lazy val mapMyKeyMyValueFormat: Format[collection.immutable.Map[MyKey, MyValue]] = new Format[collection.immutable.Map[MyKey, MyValue]] {
  override def writes(obj: collection.immutable.Map[MyKey, MyValue]): JsValue = Json.toJson(obj.map {
    case (key, value) ⇒ Json.toJson(key) -> Json.toJson(value)
  })

  override def reads(json: JsValue): JsResult[collection.immutable.Map[MyKey, MyValue]] = ???
}

我不知道如何编写正确的 reads 函数。有没有更简单的方法呢?我也不满意我的 writes 功能。

谢谢!

writes 方法不起作用的原因是因为您正在将 Map[MyKey, MyValue] 转换为 Map[JsValue, JsValue],但您无法将其序列化为 JSON . JSON 键必须是字符串,因此您需要某种方法将 MyKey 转换为某个唯一的 String 值。否则你会尝试序列化这样的东西:

{"key": "keyName"} : {"value": "myValue"}

无效 JSON。

如果 MyKey 像您问题中所述的那样简单,则可以工作:

def writes(obj: Map[MyKey, MyValue]): JsValue = Json.toJson(obj.map {
    case (key, value) => key.key -> Json.toJson(value)
})                     //   ^ must be a String

Play 将知道如何序列化 Map[String, MyValue],给定适当的 Writes[MyValue]

但我不确定这就是您想要的。因为它产生这个:

scala> Json.toJson(myNewMessage)
res0: play.api.libs.json.JsValue = {"key1":{"value":"value1"},"key2":{"value":"value2"}}

如果这是你想要的输出:

{ "key1": "value1", "key2": "value2" }

那么你的 Writes 应该看起来更像这样:

def writes(obj: Map[MyKey, MyValue]): JsValue = {
    obj.foldLeft(JsObject(Nil)) { case (js, (key, value)) =>
         js ++ Json.obj(key.key -> value.value)
    }
}

产生这个:

scala> writes(myNewMessage)
res5: play.api.libs.json.JsValue = {"key1":"value1","key2":"value2"}
只要 MyKeyMyValue 的结构相同,

Reads 就很容易,否则我不知道你想要它做什么。这在很大程度上取决于您想要的实际结构。照原样,我建议利用现有 Reads[Map[String, String]] 并将其转换为您想要的类型。

def reads(js: JsValue): JsResult[Map[MyKey, MyValue]] = {
    js.validate[Map[String, String]].map { case kvMap =>
        kvMap.map { case (key, value) => MyKey(key) -> MyValue(value) }
    }
}

如果不了解数据的实际结构,就很难看到其他内容。一般来说,我不必序列化和反序列化 Maps.