play json in scala: deserializing json with unknown fields without losing them

ⅰ亾dé卋堺 提交于 2021-02-11 08:35:33

问题


consider i have a json as following:

 {
  "a": "aa",
  "b": "bb",
  "c": "cc",
  "d": "dd", // unknown in advance
  "e": { //unknown in advance
    "aa": "aa"
  }
}

i know for sure that the json will contain a,b,c but i've no idea what other fields this json may contain.

i want to serialize this JSON into a case class containing a,b,c but on the other hand not to lose the other fields (save them in a map so the class will be deserialized to the same json as received).

ideas?


回答1:


One option is to capture the "unknown" fields in a Map[String,JsValue], from which you can later extract values if you need them.

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

Likewise, you can do a Writes or a Format like so:

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

Note: it looks a bit confusing because you give the format for Map[String,JsValue] an explicit Reads as an argument (Reads.map), which you then transform (using the .map method) to remove the already-captures values.




回答2:


You can use a custom Reads for this, something like:

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


来源:https://stackoverflow.com/questions/39096202/play-json-in-scala-deserializing-json-with-unknown-fields-without-losing-them

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!