1

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
}
4
  • 3
    Can you just use JsonSlurper and then first array would just be [Application1.it, Application1.ft ... Etc] and similar with Application 2 Commented Mar 17, 2020 at 20:51
  • 2
    It would help if you showed exactly the output you want, exactly what you've tried, and exactly how that failed Commented Mar 17, 2020 at 20:53
  • Or if you want the text value, I guess it would be like [Application1.it[0], Application1.ft[0]..... ] Though I haven't tried this out, just a guess hence comment rather than answer Commented Mar 17, 2020 at 20:54
  • @Chris and tim_yates ..Thank you for your suggestion. I updated my expected output. Commented Mar 18, 2020 at 1:01

2 Answers 2

3

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]]
Sign up to request clarification or add additional context in comments.

4 Comments

Thank you so much for your time answering my query. I updated my expected output. Could you please help ? Also im quite new to Groovy which makes me difficult to understand your answer. Is there any other way this can be programmed ? Thank you already!
@user1557960, Please see edits based on your clarification of output requirements. Cheers!
Hi Phil Wrage, Thanks for all your inputs. It really helped. I have done some small modifications and made it work.
Updated my answer in the question itself.
0

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]]

2 Comments

Thanks. I am getting this error. "hudson.remoting.ProxyException: groovy.lang.MissingMethodException: No signature of method: java.util.ArrayList.tap() is applicable for argument types: (org.jenkinsci.plugins.workflow.cps.CpsClosure2) values: [org.jenkinsci.plugins.workflow.cps.CpsClosure2@5c09fe2e]"
tap is optional here. I rewrote for old groovy versions

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.