1

考虑我有一个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 的案例类,但另一方面不要丢失其他字段(将它们保存在地图中,以便将类反序列化为与接收到的相同的 json)。

想法?

4

2 回答 2

2

一种选择是捕获 a 中的“未知”字段,Map[String,JsValue]以后可以在需要时从中提取值。

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"))

同样,您可以这样WritesFormat

// 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))

注意:它看起来有点令人困惑,因为您将formatforMap[String,JsValue]显式Reads作为参数 ( Reads.map),然后您对其进行转换(使用.map 方法)以删除已经捕获的值。

于 2016-08-23T08:55:12.017 回答
1

您可以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)
    }
  }
}
于 2016-08-23T08:54:00.727 回答