在scala中播放json:使用未知字段对json进行反序列化而不会丢失它们

时间:2016-08-23 08:35:09

标签: json scala playframework play-json

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

想法?

2 个答案:

答案 0 :(得分: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)
    }
  }
}

答案 1 :(得分:1)

一个选项是捕获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))

注意:它看起来有点令人困惑,因为您为format Map[String,JsValue]提供了一个显式Reads作为参数(Reads.map),然后您将其转换(使用.map 方法)删除已捕获的值。