如何在 scala 中使用 play-json 从 Json 中提取额外的(键,值)?
How to extract extra (key,value) from Json using play-json in scala?
我有以下场景:
case class Person(id: Int, name: String)
val json = Json.obj("id" -> 1, "name" -> "John", "address"-> "Paris", "contact" -> "1234")
在这里,我想从 json 中提取额外的 (key,value) 即 { "address"-> "Paris", "contact" -> "1234"} 不属于 人 。
到目前为止,我已经开发了以下方法:
case class Person(id: Int, name: String)
val personReads = Json.reads[Person]
val personWrites = Json.writes[Person]
val json = Json.obj("id" -> 1, "name" -> "John", "address"-> "Paris", "contact" -> "1234")
val person: Person = personReads.reads(json).get
// This person json does not have extra fields
val personJson: JsObject = personWrites.writes(person).asInstanceOf[JsObject]
val extraKeys = json.keys.diff(personJson.keys)
val extraJson = extraKeys.foldLeft(Json.obj()){(result,key) =>
result.+(key -> json.\(key).get)}
// {"address":"Paris","contact":"1234"}
这行得通,但在这里我必须做很多 json 到大小写 class 的转换。在这种情况下提取额外 (key,value) 的最佳方法是什么?
如果你想让它对任何案例都通用 class 并且不想对自定义 Reads
做任何花哨的事情,你可以使用反射或 shapeless 来提取案例 class 名称,然后从您要解析的对象中删除那些。
例如使用反射,这只会创建一个 case class 实例一次,根本不需要 Writes
:
import play.api.libs.json._
import scala.reflect.runtime.universe._
def withExtra[A: Reads: TypeTag]: Reads[(A, JsObject)] = {
val ccFieldNames = typeOf[A].members.collect {
case m: MethodSymbol if m.isCaseAccessor => m.name.toString
}.toVector
for {
jsObj <- implicitly[Reads[JsObject]]
a <- implicitly[Reads[A]]
filteredObj = ccFieldNames.foldLeft(jsObj)(_ - _)
} yield (a, filteredObj)
}
并使用它,例如像这样:
case class Person(id: Int, name: String)
case class Location(id: Int, address: String)
val json = Json.obj("id" -> 1, "name" -> "John", "address"-> "Paris", "contact" -> "1234")
implicit val pReads = Json.reads[Person]
implicit val lReads = Json.reads[Location]
assert { withExtra[Person].reads(json).get == (
Person(1, "John"),
Json.obj("address"-> "Paris", "contact" -> "1234")
) }
assert { withExtra[Location].reads(json).get == (
Location(1, "Paris"),
Json.obj("name" -> "John", "contact" -> "1234")
) }
可运行代码可用there
我有以下场景:
case class Person(id: Int, name: String)
val json = Json.obj("id" -> 1, "name" -> "John", "address"-> "Paris", "contact" -> "1234")
在这里,我想从 json 中提取额外的 (key,value) 即 { "address"-> "Paris", "contact" -> "1234"} 不属于 人 。
到目前为止,我已经开发了以下方法:
case class Person(id: Int, name: String)
val personReads = Json.reads[Person]
val personWrites = Json.writes[Person]
val json = Json.obj("id" -> 1, "name" -> "John", "address"-> "Paris", "contact" -> "1234")
val person: Person = personReads.reads(json).get
// This person json does not have extra fields
val personJson: JsObject = personWrites.writes(person).asInstanceOf[JsObject]
val extraKeys = json.keys.diff(personJson.keys)
val extraJson = extraKeys.foldLeft(Json.obj()){(result,key) =>
result.+(key -> json.\(key).get)}
// {"address":"Paris","contact":"1234"}
这行得通,但在这里我必须做很多 json 到大小写 class 的转换。在这种情况下提取额外 (key,value) 的最佳方法是什么?
如果你想让它对任何案例都通用 class 并且不想对自定义 Reads
做任何花哨的事情,你可以使用反射或 shapeless 来提取案例 class 名称,然后从您要解析的对象中删除那些。
例如使用反射,这只会创建一个 case class 实例一次,根本不需要 Writes
:
import play.api.libs.json._
import scala.reflect.runtime.universe._
def withExtra[A: Reads: TypeTag]: Reads[(A, JsObject)] = {
val ccFieldNames = typeOf[A].members.collect {
case m: MethodSymbol if m.isCaseAccessor => m.name.toString
}.toVector
for {
jsObj <- implicitly[Reads[JsObject]]
a <- implicitly[Reads[A]]
filteredObj = ccFieldNames.foldLeft(jsObj)(_ - _)
} yield (a, filteredObj)
}
并使用它,例如像这样:
case class Person(id: Int, name: String)
case class Location(id: Int, address: String)
val json = Json.obj("id" -> 1, "name" -> "John", "address"-> "Paris", "contact" -> "1234")
implicit val pReads = Json.reads[Person]
implicit val lReads = Json.reads[Location]
assert { withExtra[Person].reads(json).get == (
Person(1, "John"),
Json.obj("address"-> "Paris", "contact" -> "1234")
) }
assert { withExtra[Location].reads(json).get == (
Location(1, "Paris"),
Json.obj("name" -> "John", "contact" -> "1234")
) }
可运行代码可用there