不可变映射(反)序列化 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"}
只要 MyKey
和 MyValue
的结构相同, 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) }
}
}
如果不了解数据的实际结构,就很难看到其他内容。一般来说,我不必序列化和反序列化 Map
s.
我有以下(简化的)结构:
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"}
只要 MyKey
和 MyValue
的结构相同, 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) }
}
}
如果不了解数据的实际结构,就很难看到其他内容。一般来说,我不必序列化和反序列化 Map
s.