2
1
0

I've encountered a JSON structure like the following:

{
    "params": {
        "firstName": {
            "label": "First Name",
            "type": "String"
        },
        "surname": {
            "label": "Last Name",
            "type": "String"
        }
    }
}

Note that the params element contains a list of similar sub elements but it is not represented as an array as you would expect. The names of those sub elements cannot be predicted so we can't query them explicitly.

I want to iterate through the sub elements using the Path class in a manner that allows me to access the name of the element (e.g. "firstName", "surname") but don't see an obvious way to do that. The following code shows what I'd like to achieve but doesn't compile as there is no attribute called name on the Path object.

Path jsonPath = new Path(JSON_DATA)
jsonPath.paths("params.*").each{
    println it.name + " - [" + it.val("type") + "] " + it.val("label")
}

The result should be output as follows:

firstName - [String] First Name
surname - [String] Last Name

Anyone got advice on how to achieve this?

    CommentAdd your comment...

    2 answers

    1.  
      3
      2
      1

      Found the answer thanks to Radoslav Ivanov

          Path jsonPath = new Path(JSON_DATA)
          ((Map) jsonPath.val("params")).each {
              println it.key + " - [" + jsonPath.val("params.${it.key}.type") + "] " + jsonPath.val("params.${it.key}.label")
          }
      1. Jye Cusch

        An alternative to casting the return value from jsonPath.val("params") to a Map is to specify the type and name of the value passed into the Closure. E.g.

        Path jsonPath = new Path(JSON_DATA)
        jsonPath.val("params").each { Map.Entry obj
            println obj.key + " - [" + jsonPath.val("params.${obj.key}.type") + "] " + jsonPath.val("params.${obj.key}.label")
        }


        Just mentioning this because at first I didn't notice the type cast to Map and type checking failed.

      CommentAdd your comment...
    2.  
      2
      1
      0

      Hi Ben,

      Wouldn't just plain 'ol Groovy be neater?

      String JSON_DATA = """{
          "params": {
              "firstName": {
                  "label": "First Name",
                  "type": "String"
              },
              "surname": {
                  "label": "Last Name",
                  "type": "String"
              }
          }
      }
      """
      def jsonData = new groovy.json.JsonSlurper().parseText(JSON_DATA)
      println jsonData.params.subMap(jsonData.params.keySet())

      Gives you this output:

      [firstName:[label:First Name, type:String], surname:[label:Last Name, type:String]]

      And you only need to manage the name of the outer map.

      credit: http://mrhaki.blogspot.co.uk/2009/10/groovy-goodness-getting-submap-from-map.html
      1. Ben Warner

        Actually the reason I was looking at the Path class is because I was looking for an alternative to JsonSlurper as it re-orders the parameters alphabetically according to their key which was entirely undesirable for my application. Not sure why it does this and there doesn't seem to be a way to turn it off.

        In the example JSON_DATA above if you put type above label you will still get the same output with type appearing after label in the output.

      CommentAdd your comment...