Using lift-json, is there an easy way to extract and traverse a list?

Fred Haslam picture Fred Haslam · Feb 22, 2011 · Viewed 7.1k times · Source

I think I may be missing something fundamental from the list-json xpath architecture. The smoothest way I've been able to extract and traverse a list is shown below. Can someone please show me a better technique:

class Example {

    @Test
    def traverseJsonArray() {

        def myOperation(kid:JObject) = println("kid="+kid)

        val json = JsonParser.parse("""
            {   "kids":[
                {"name":"bob","age":3},
                {"name":"angie","age":5},
            ]}
        """)

        val list = ( json \\ "kids" ).children(0).children
        for ( kid <- list ) myOperation(kid.asInstanceOf[JObject])

    }

}

Answer

Joni picture Joni · Feb 22, 2011

If at all possible you should upgrade to Lift JSON 2.3-M1 (http://www.scala-tools.org/repo-releases/net/liftweb/lift-json_2.8.1/2.3-M1/). It contains two important improvements, the other affecting the path expressions.

With 2.3 the path expressions never return JFields, instead the values of JFields are returned directly. After that your example would look like:

val list = (json \ "kids").children
for ( kid <- list ) myOperation(kid.asInstanceOf[JObject])

Lift JSON provides several styles to parse values from JSON: path expressions, query comprehensions and case class extractions. It is possible to mix and match these styles and to get the best results we often do. For completeness sake I'll give you some variations of the above example to get a better intuition of these different styles.

// Collect all JObjects from 'kids' array and iterate
val JArray(kids) = json \ "kids"
kids collect { case kid: JObject => kid } foreach myOperation

// Yield the JObjects from 'kids' array and iterate over yielded list
(for (kid@JObject(_) <- json \ "kids") yield kid) foreach myOperation

// Extract the values of 'kids' array as JObjects
implicit val formats = DefaultFormats
(json \ "kids").extract[List[JObject]] foreach myOperation

// Extract the values of 'kids' array as case classes
case class Kid(name: String, age: Int)

(json \ "kids").extract[List[Kid]] foreach println

// Query the JSON with a query comprehension
val ks = for { 
  JArray(kids) <- json
  kid@JObject(_) <- kids
} yield kid 
ks foreach myOperation