4

I have the following code in Scala. My goal is to extract the value(s) of a key without knowing how many and how deep they are.


    import org.json4s.jackson.JsonMethods._
    import org.json4s.{DefaultFormats, JField, JObject, JString, JValue}

    object jsonLift {

        implicit val formats = DefaultFormats

        val js = parse(
            """
    {
      "url": "imap.yahoo.com",
        "credentials": {
        "username": "myusername",
        "password": "mypassword"
         },
        "some key":{
            "other key": {
                "username": "hello"
            }
        }
    }
            """)


            def getElem(elem: String, json:JValue) = for {
                    JObject(child) <- json
                    JField(elem, JString(value)) <- child       // this does not return the expected result
    //              JField("username", JString(value)) <- child // this returns the expected result
                } yield  value


        def main(args: Array[String]) {

            val result = getElem("username", js)
            println(result)

        }
    }

The result of the above code is List(imap.yahoo.com, myusername, mypassword, hello) which is not what I am expecting. My expected result is List(myusername, hello).

However, if I change the variable elem, directly inside the method getElem, with the key (as string) that I am interested in (Eg: "username") I get the expected result: List(myusername, hello) which are all the values of the key "username".

How can I get the expected list of value(s) by calling the method getElem with the name of the JSON key as argument? Eg: getElem("JSON key", json)

Thank you!

geo
  • 516
  • 5
  • 12

2 Answers2

3

Change getElem to

def getElem(elem: String, json:JValue) = for {
  JObject(child) <- json
  JField(`elem`, JString(value)) <- child
} yield  value

Without the backticks around elem on the RHS, you are binding the first element of type JField = (String, JValue) to a new name elem and shadowing the method parameter elem

wmmeyer
  • 466
  • 3
  • 4
  • I did not know that using backticks would work like that and match in this way. Good to know. Thanks! – Mike Curry Aug 07 '16 at 16:16
  • I did not know that either. Looks like a scala-esque solution. Thank you !!! Indeed it returns the expected result. – geo Aug 07 '16 at 23:00
1

So, the issue here is that when you use:

JField(elem, JString(value)) <- child 

inside your for comprehension, this new definition of elem shadows the elem which you passed into the getElem method.

I guess your intent was that this would match if the value elem passed into getElem was the same as the name of the JField. This is not how it works however, and instead, the symbol elem is bound to the key name.

You can achieve what you want with a small alteration to the line above as follows:

JField(key, JString(value)) <- child if key == elem

which will bind the value to value only if the name of the JField matches elem.

Mike Curry
  • 1,609
  • 1
  • 9
  • 12