Sivakumar Kayaroganam
Sivakumar Kayaroganam

Reputation: 360

Parsing nested JSON in groovy

I would like to parse the below nested JSON in Groovy and get the values of "it","ft","stg","prd" for each application and store in separate array.Can someone help please ?

    {
        "Application1": {
            "environments": {
                "it": [
                    "server1"
                ],
                "ft": [
                    "server2"
                ],
                "stg": [
                    "server3"
                ],
                "prd": [
                    "server4"
                ]
            },
            "war-path" : "/opt/tomcat/",
            "war-name" : "Application1"
        },
        "Application2": {
            "environments": {
                "it": [
                    "serverA"
                ],
                "ft": [
                    "serverB"
                ],
                "stg": [
                    "serverC"
                ],
                "prd": [
                    "serverD"
                ]
            },
            "war-path" : "/var/lib",
            "war-name" : "Application2"
        }
    }
}

Expected output something like below in separate list for each environment. Also the 1st level(Application1,Application2..) will be dynamic always

it = [server1,serverA]
ft = [server2,serverB]
stg = [server3, serverC]
prd = [server4,serverD]

Updated: After deriving expected answer with the inputs from Philip Wrage.

def projects = readJSON file: "${env.WORKSPACE}//${infrafile}"
def json_str = JsonOutput.toJson(projects)
def json_beauty = JsonOutput.prettyPrint(json_str)
def envlist = ["it","ft","stg","prd"]
def fileListResult = []


for (envname in envlist) {
    servers=serverlist(json_beauty,envname)
    println(servers)
}

def serverlist(json_beauty,envname){
    def jsonSlurper = new JsonSlurper()
    def jsonMap = jsonSlurper.parseText(json_beauty)

    assert jsonMap instanceof Map
    jsonMap.each { appName, appDetails ->

        assert appDetails instanceof Map
        appDetails.environments.each { envName, servers ->
            for (items in servers{
                if (envName == "${envname}"){
                    fileListResult.add(items)
                }
            }
        }        
    }
    return fileListResult
}

Upvotes: 1

Views: 8321

Answers (2)

Philip Wrage
Philip Wrage

Reputation: 1559

As suggested by @Chris, you can use the built-in JsonSlurper to parse the JSON and then navigate the parsed results as a Map.

In the following example I show how you can simply print the results to the console. However, using this as a guide, you can see how to extract the data into whatever kind of data structure that suits your purpose.

Assume that you have the JSON in a String variable jsonText. You may be pulling in from a file or an HTTP POST or whatever your app requires. I used the following code to set this value for testing.

def jsonText = """
{
    "Application1": {
    "environments": {
        "it": [
                "server1"
        ],
        "ft": [
                "server2"
        ],
        "stg": [
                "server3"
        ],
        "prd": [
                "server4"
        ]
    },
    "war-path" : "/opt/tomcat/",
    "war-name" : "Application1"
},
    "Application2": {
    "environments": {
        "it": [
                "serverA"
        ],
        "ft": [
                "serverB"
        ],
        "stg": [
                "serverC"
        ],
        "prd": [
                "serverD"
        ]
    },
    "war-path" : "/var/lib",
    "war-name" : "Application2"
}
}
"""

The meat of the solution then follows. Parse the JSON text into a Map, and then iterate over the entries in that Map, performing whatever operations you require. The servers for each environment will already be contained within a List.

import groovy.json.JsonSlurper

def jsonSlurper = new JsonSlurper()
def jsonMap = jsonSlurper.parseText(jsonText)

assert jsonMap instanceof Map

jsonMap.each { appName, appDetails ->
    println "Application: $appName"
    assert appDetails instanceof Map
    appDetails.environments.each { envName, servers ->
        assert servers instanceof List
        println "\tEnvironment: $envName"
        println "\t\t$servers"
    }
}

From this code I obtain the following console output.

Application: Application1
    Environment: it
        [server1]
    Environment: ft
        [server2]
    Environment: stg
        [server3]
    Environment: prd
        [server4]
Application: Application2
    Environment: it
        [serverA]
    Environment: ft
        [serverB]
    Environment: stg
        [serverC]
    Environment: prd
        [serverD]

EDIT (based on clarification of required output):

import groovy.json.JsonSlurper

def jsonText = "\n{\n    \"Application1\": {\n    \"environments\": {\n        \"it\": [\n                \"server1\"\n        ],\n        \"ft\": [\n                \"server2\"\n        ],\n        \"stg\": [\n                \"server3\"\n        ],\n        \"prd\": [\n                \"server4\"\n        ]\n    },\n    \"war-path\" : \"/opt/tomcat/\",\n    \"war-name\" : \"Application1\"\n},\n    \"Application2\": {\n    \"environments\": {\n        \"it\": [\n                \"serverA\"\n        ],\n        \"ft\": [\n                \"serverB\"\n        ],\n        \"stg\": [\n                \"serverC\"\n        ],\n        \"prd\": [\n                \"serverD\"\n        ]\n    },\n    \"war-path\" : \"/var/lib\",\n    \"war-name\" : \"Application2\"\n}\n}\n"

def jsonSlurper = new JsonSlurper()
def jsonMap = jsonSlurper.parseText(jsonText)

def result = [:]

jsonMap.each { appName, appDetails ->
    appDetails.environments.each { envName, servers ->
        if ( !result.containsKey(envName) ) {
            result.put(envName, [])
        }
        result.get(envName).addAll(servers)
    }
}
println result

Results are a Map where the keys are the various environments specified within JSON file, and the values are Lists of servers associated with those environments across all applications. You can access any List individually with something like result.stg, or assign these the different variables later if that's desired/required (def stg = result.stg).

[it:[server1, serverA], ft:[server2, serverB], stg:[server3, serverC], prd:[server4, serverD]]

Upvotes: 3

cfrick
cfrick

Reputation: 37008

If you want all environments, you can use the spread operator to take all environments from the values. Next you have to merge the maps on the keys. E.g.

def json = """ { "Application1": { "environments": { "it": [ "server1" ], "ft": [ "server2" ], "stg": [ "server3" ], "prd": [ "server4" ] }, }, "Application2": { "environments": { "it": [ "serverA" ], "ft": [ "serverB" ], "stg": [ "serverC" ], "prd": [ "serverD" ] }, } } }"""

def data = new groovy.json.JsonSlurper().parseText(json)

println data.values()*.environments.inject{ a, b -> 
    b.inject(a.withDefault{[]}) { m, kv -> 
        // with groovy 2.5+: m.tap{ get(kv.key).addAll(kv.value) }
        m[kv.key].addAll(kv.value); m
    } 
}
// → [it:[server1, serverA], ft:[server2, serverB], stg:[server3, serverC], prd:[server4, serverD]]

Upvotes: 0

Related Questions