简体   繁体   中英

How to extract extra (key,value) from Json using play-json in scala?

I have a following scenario:

case class Person(id: Int, name: String)
val json = Json.obj("id" -> 1, "name" -> "John", "address"-> "Paris", "contact" -> "1234")

Here, I want to extract extra (key,value) from json ie {"address"-> "Paris", "contact" -> "1234"} that do not belong to Person .

I have developed following approach so far:

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

This works but here I have to do a lot of json to case class conversions. What would be the best way to extract extra (key,value) in this scenario ?

If you want to make it general for any case class and not doing anything fancy with custom Reads , you can use reflection or shapeless to extract case class names and then remove those from an object you're trying to parse.

Eg using reflection, this only creates a case class instance once and does not require Writes at all:

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

And use it eg like so:

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

Runnable code is available there

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM