groovyjsonslurper

Groovy JSONSlurper - How to access multiple level objects/arrays?


I have a project where I'll regularly be dealing with JSON files that are up to five levels deep. The elements will always be the same, but each level may come as an array or as an individual object.

What is the best way to iterate through these? I had originally intended to try looping through using JSONPath but am just learning about maps and wondering if that would be easier/cleaner?

By default I'm only getting one key, and it's for the root element. Is there a way to add keys for the other levels?

Below is a "simplified" of the JSON I'm working with. All of the HL#Types can be arrays. There is sometimes an HL5.

{
"HL1": {
    "HL1Type": [
        {
            "Item1": "123",
            "Item2": "abc",
            "to_HL2": {
                "HL2Type": {
                    "ActualDeliveryQuantity": "6000.000",
                    "DeliveryQuantityUnit": "KP",
                    "to_HL3": {
                        "HL3Type": {
                            "HandlingUnitHeight": "10.000",
                            "HandlingUnitLength": "20.000",
                            "to_HL4": {
                                "HL4Type": {
                                    "MATERIAL": "ABC123",
                                    "to_HL5": ""
                                }
                            },
                            "UnitOfMeasureDimension": ""
                        }
                    }
                }
            }
        },
        {
            "Item1": "456",
            "Item2": "def",
            "to_HL2": {
                "HL2Type": {
                    "ActualDeliveryQuantity": "7000.000",
                    "DeliveryQuantityUnit": "KP",
                    "to_HL3": {
                        "HL3Type": [
                            {
                                "HandlingUnitHeight": "10.000",
                                "HandlingUnitLength": "20.000",
                                "to_HL4": {
                                    "HL4Type": {
                                        "MATERIAL": "DEF456",
                                        "to_HL5": ""
                                    }
                                },
                                "UnitOfMeasureDimension": ""
                            },
                            {
                                "HandlingUnitHeight": "20.000",
                                "HandlingUnitLength": "30.000",
                                "to_HL4": {
                                    "HL4Type": {
                                        "MATERIAL": "GHI789",
                                        "to_HL5": ""
                                    }
                                },
                                "UnitOfMeasureDimension": ""
                            }
                        ]
                    }
                }
            }
        }
    ]
}

I am new to maps, so what I have so far is very basic.

import java.util.Properties;
import java.io.InputStream;
import groovy.json.JsonSlurper;
import groovy.json.JsonOutput;

def inputFile = new File("C:\\ExampleJSON.json")
def InputJSON = new JsonSlurper().parseFile(inputFile, 'UTF-8')
InputJSON = InputJSON.HL1.HL1Type

InputJSON.eachWithIndex{ Item, idx -> 
    println "$idx: $Item" }

InputJSON.to_HL2.HL2Type.eachWithIndex{ Item, idx -> 
    println "$idx: $Item" }

I can get everything to display, but I'm looking for a way to iterate to I can get something like:

"123, abc, 6000, KP, 10, 20, ABC123"
"456, def, 7000, KP, 10, 20, DEF456"
"456, def, 7000, KP, 20, 30, GHI789"

I'm not worried about getting the data formatted exactly that way, just looking for tips on how to actually get at the data that I need.


Solution

  • Ok I'm going to take a a stab and something quick and dirty. I don't think you know what you are after so this is just throwing some spa-groovy on the wall and seeing what sticks.

    import java.util.Properties;
    import java.io.InputStream;
    import groovy.json.JsonSlurper;
    import groovy.json.JsonOutput;
    
    def input = new File("C:\\ExampleJSON.json")
    def json = new JsonSlurper().parseFile(input, 'UTF-8')
    
    List<Map<String,Object>> items = json.HL1.HL1Type.collect { item ->
       [
          Item1: item.Item1,
          Item2: item.Item2,
          ActualDeliveryQuantity: item.to_HL2.HL2Type.ActualDeliveryQuantity,
          DeliveryQuantityUnit: item.to_HL2.HL2Type.DeliveryQuantityUnit,
          HandlingUnitHeight: item.to_HL2.HL2Type.to_HL3.HL3Type.HandlingUnitHeight,
          HandlingUnitLength: item.to_HL2.HL2Type.to_HL3.HL3Type.HandlingUnitLength,
          Material: item.to_HL2.HL2Type.to_HL3.HL3Type.to_HL4.HL4Type.MATERIAL
       ]      
    }
    
    items.each { row ->
       println( "${row.Item1} ${row.Item2} ${row.ActualDeliveryQuantity} ${row.DeliveryQuantityUnit} ${row.HandlingUnitHeight} ${row.HandlingUnitLength} ${row.Material}" )
    }
    

    You could simplify the reading of this by grabbing HL2Type and HL3Type into local vars which makes the nested portions much easier to read.

    List<Map<String,Object>> items = json.HL1.HL1Type.collect { item ->
       def hl2Type = item.to_HL2.HL2Type
       def hl3Type = item.to_HL2.HL2Type.to_HL3.HL3Type
       def hl4Type = item.to_HL2.HL2Type.to_HL3.HL3Type.to_HL4.HL4Type
       [
          Item1: item.Item1,
          Item2: item.Item2,
          ActualDeliveryQuantity: hl2Type.ActualDeliveryQuantity,
          DeliveryQuantityUnit: hl2Type.DeliveryQuantityUnit,
          HandlingUnitHeight: hl3Type.HandlingUnitHeight,
          HandlingUnitLength: hl3Type.HandlingUnitLength,
          Material: hl4Type.MATERIAL
       ]      
    }
    

    But essential what this is doing is iterating over the array for the JSON and return a List of Map objects which are flattened representations of your JSON objects. The keys of the Map match the field names from which the data came from, and the values are the data.

    This isn't a generic JSON parser, but just a skeleton for you to slide in different paths to objects to produce simple scripts. Notice doing things this ways separates the visual representation from the processing. Iterating the extracted set of maps could be rendered into any format you wish without impacting the parsing of the JSON.

    This is just a simple straight forward way to write your scripts, but you still have to write them. No magic here.