不可变的Map(de)序列化到/来自Play JSON

时间:2015-01-26 16:16:00

标签: json serialization dictionary playframework deserialization

我有以下(简化)结构:

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功能也不满意。

THX!

1 个答案:

答案 0 :(得分:1)

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

在给定相应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"}
只要ReadsMyKey的结构相同,

MyValue就很容易,否则我不知道你想要它做什么。它非常依赖于您想要的实际结构。因此,我建议利用现有的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) }
    }
}

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