简体   繁体   中英

Extract fields in nested json using Scala's json4s

Given a parsed (via json4s) json string eg the following org.json4s.JValue :

val data: JValue = parse("""{
    "important_field" : "info",
    "some_junk" : 12345,
    "interesting_stuff" : {
        "pi" : 3.14,
        "e" : 2.72
    }
}""")

I can selectively extract the information I need:

case class Data(important_field: String) 
val extracted: Data = data.extract[Data]

giving extracted: Data = Data(info) . Further, I can extract information from a nested json object with another case class:

case class Stuff(pi: Double)
case class Data(important_field: String, interesting_stuff: Stuff)
val extracted: Data = data.extract[Data]

which gives me extracted: Data = Data(info,Stuff(3.14)) . But now, if I want to access fields in the nested object, I need to use Stuff , my inner case class:

val pi: Double = extracted.interesting_stuff.pi

I'd prefer to drop the nesting, so Data is flat, ie I want to access the (originally) nested fields like this:

val pi: Double = extracted.pi

In this trivial case, using the nested class is not that painful, but when there are multiple levels of nesting it's frustrating to have to maintain that nesting in my object. Is there a typical approach for dealing with this? Some fancy Scala trick?

Nested json structures can be transformed before mapping json content to your own case class:

object NestedJsonSpec {
  case class Data(important_field: String, pi: Double)

  implicit val formats = DefaultFormats // Brings in default date formats etc.

  def extract(source: JsonInput) : Data = {
    parse(source).transformField({
      case JField("interesting_stuff", jv) => ("pi", jv \ "pi")
    }).extract[Data]
  }
}

As a result one does not have to define a nested case class Stuff .

Dependencies used in the code were:

scalaVersion := "2.11.8"

libraryDependencies ++= {
  Seq(
    "org.json4s" %% "json4s-ext" % "3.5.0",
    "org.json4s" %% "json4s-native" % "3.5.0",
    "org.scalatest" %% "scalatest" % "2.2.6" % Test
  )
}

Here is the related github project with the unit test .

you can define a getter to the nested field in your outer case class

case class Data(important_field: String, interesting_stuff: Stuff) {
    def pi: Double = interesting_stuff.pi
}

// now you'll be able to write
val pi: Double = extracted.pi

the downside is that you have to do this by hand for each nested field you want to access easily

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