在 scala 中播放 json:使用未知字段反序列化 json 而不会丢失它们
play json in scala: deserializing json with unknown fields without losing them
考虑我有一个 json 如下:
{
"a": "aa",
"b": "bb",
"c": "cc",
"d": "dd", // unknown in advance
"e": { //unknown in advance
"aa": "aa"
}
}
我确定 json 将包含 a、b、c,但我不知道这个 json 可能包含哪些其他字段。
我想将此 JSON 序列化为包含 a、b、c 的案例 class,但另一方面又不想丢失其他字段(将它们保存在地图中,因此 class 将被反序列化为与接收到的相同的 json。
想法?
您可以为此使用自定义 Reads
,例如:
import play.api.libs.json._
import play.api.libs.functional.syntax._
case class MyData(a: String, b: String, c:String, other: Map[String, JsValue])
object MyData {
val abcReader: Reads[(String, String, String)] = (
(JsPath \ "a").read[String] and
(JsPath \ "b").read[String] and
(JsPath \ "c").read[String]
).tupled
implicit val reader: Reads[MyData] = Reads { json =>
abcReader.reads(json).map {
case (a, b, c) =>
val other = json.as[JsObject].value -- Seq("a", "b", "c")
MyData(a, b, c, other.toMap)
}
}
}
一个选项是捕获 Map[String,JsValue]
中的 "unknown" 字段,您可以在以后需要时从中提取值。
case class MyClass(a: String, b: String, c: String, extra: Map[String, JsValue])
implicit val reads: Reads[MyClass] = (
(__ \ "a").read[String] and
(__ \ "b").read[String] and
(__ \ "c").read[String] and
__.read[Map[String, JsValue]]
.map(_.filterKeys(k => !Seq("a", "b", "c").contains(k)))
)(MyClass.apply _)
// Result:
// MyClass(aa,bb,cc,Map(e -> {"aa":"aa"}, d -> "dd"))
同样,您可以像这样执行 Writes
或 Format
:
// And a writes...
implicit val writes: Writes[MyClass] = (
(__ \ "a").write[String] and
(__ \ "b").write[String] and
(__ \ "c").write[String] and
__.write[Map[String, JsValue]]
)(unlift(MyClass.unapply _))
// Or combine the two...
implicit val format: Format[MyClass] = (
(__ \ "a").format[String] and
(__ \ "b").format[String] and
(__ \ "c").format[String] and
__.format[Map[String, JsValue]](Reads
.map[JsValue].map(_.filterKeys(k => !Seq("a", "b", "c").contains(k))))
)(MyClass.apply, unlift(MyClass.unapply))
注意:它看起来有点混乱,因为你给 format
for Map[String,JsValue]
一个明确的 Reads
作为参数 (Reads.map
),然后你转换 (使用 .map
方法 ) 删除已经捕获的值。
考虑我有一个 json 如下:
{
"a": "aa",
"b": "bb",
"c": "cc",
"d": "dd", // unknown in advance
"e": { //unknown in advance
"aa": "aa"
}
}
我确定 json 将包含 a、b、c,但我不知道这个 json 可能包含哪些其他字段。
我想将此 JSON 序列化为包含 a、b、c 的案例 class,但另一方面又不想丢失其他字段(将它们保存在地图中,因此 class 将被反序列化为与接收到的相同的 json。
想法?
您可以为此使用自定义 Reads
,例如:
import play.api.libs.json._
import play.api.libs.functional.syntax._
case class MyData(a: String, b: String, c:String, other: Map[String, JsValue])
object MyData {
val abcReader: Reads[(String, String, String)] = (
(JsPath \ "a").read[String] and
(JsPath \ "b").read[String] and
(JsPath \ "c").read[String]
).tupled
implicit val reader: Reads[MyData] = Reads { json =>
abcReader.reads(json).map {
case (a, b, c) =>
val other = json.as[JsObject].value -- Seq("a", "b", "c")
MyData(a, b, c, other.toMap)
}
}
}
一个选项是捕获 Map[String,JsValue]
中的 "unknown" 字段,您可以在以后需要时从中提取值。
case class MyClass(a: String, b: String, c: String, extra: Map[String, JsValue])
implicit val reads: Reads[MyClass] = (
(__ \ "a").read[String] and
(__ \ "b").read[String] and
(__ \ "c").read[String] and
__.read[Map[String, JsValue]]
.map(_.filterKeys(k => !Seq("a", "b", "c").contains(k)))
)(MyClass.apply _)
// Result:
// MyClass(aa,bb,cc,Map(e -> {"aa":"aa"}, d -> "dd"))
同样,您可以像这样执行 Writes
或 Format
:
// And a writes...
implicit val writes: Writes[MyClass] = (
(__ \ "a").write[String] and
(__ \ "b").write[String] and
(__ \ "c").write[String] and
__.write[Map[String, JsValue]]
)(unlift(MyClass.unapply _))
// Or combine the two...
implicit val format: Format[MyClass] = (
(__ \ "a").format[String] and
(__ \ "b").format[String] and
(__ \ "c").format[String] and
__.format[Map[String, JsValue]](Reads
.map[JsValue].map(_.filterKeys(k => !Seq("a", "b", "c").contains(k))))
)(MyClass.apply, unlift(MyClass.unapply))
注意:它看起来有点混乱,因为你给 format
for Map[String,JsValue]
一个明确的 Reads
作为参数 (Reads.map
),然后你转换 (使用 .map
方法 ) 删除已经捕获的值。