Can we create JSON using JsonBuilder for the following JSON? - json

I want to create the below JSON using JsonBuilder.
"isOut": false,
"baleRun": {
"incData": true,
"appendCricket": [{
"min": 10,
"max": 32,
"price": "10"
}]
}
I have tried below code to create it:-
import groovy.json.*
def builder = new JsonBuilder()
def root = builder.baleRun{
incData true
builder.appendCricket [
{
min 10
max 32
price "10000"
}
]
}
Getting below error:-
groovy.lang.MissingPropertyException: No such property: appendCricket for
class: groovy.json.JsonBuilder error.
Any idea how to produce this?

The simplest way is to build a Map for the data you want, then pass this to the builder in the constructor:
import groovy.json.*
def data = [
isOut: false,
baleRun: [
incData: true,
appendCricket: [
[min: 10, max: 32, price: '10']
]
]
]
def json = new JsonBuilder(data).toString()

It is easy to create it using map, like #tim_yates suggested.
Of course, it is also possible to create the way you started with. Should be taken care for array, see in line:
def json = new groovy.json.JsonBuilder()
json {
isOut false
baleRun {
incData true
appendCricket( [
{
min 10
max 32
price "10000"
}
])
}
}
println json.toPrettyString()
Quickly try the same online demo

Related

Groovy Modifying Json

I'm trying to modify a json which I'm reading from a file:
String response = new File("response.json").text
here is what response looks like:
{
"TerminatingInstances": [
{
"InstanceId": "id",
"CurrentState": {
"Code": 32,
"Name": "shutting-down"
},
"PreviousState": {
"Code": 16,
"Name": "running"
}
}
]
}
To modify it I did as follows:
def slurped = new JsonSlurper().parseText(response)
def builder = new JsonBuilder(slurped)
builder.content.TerminatingInstances.InstanceId = "id-updated"
but I get argument type mismatch error, not sure why.
That's because it's an array of instances inside TerminatingInstances
You can either set all of them to the new id with *.:
builder.content.TerminatingInstances*.InstanceId = "id-updated"
Or set just the first one with
builder.content.TerminatingInstances[0].InstanceId = "id-updated"

how to parse CSV to JSON from 2 CSV Files in Groovy

Please help with parse CSV to JSON from 2 CSV Files in groovy
For example :
CSV1:
testKey,status
Name001,PASS
Name002,PASS
Name003,FAIL
CSV2:
Kt,Pd
PT-01,Name001
PT-02,Name002
PT-03,Name003
PT-04,Name004
I want to input in "testlist" data from CSV2.val[1..-1],CSV1.val[1..-1]
Result should be like :
{
"testExecutionKey": "DEMO-303",
"info": {
"user": "admin"
},
"tests": [
{
"TestKey": "PT-01",
"status": "PASS"
},
{
"TestKey": "PT-02",
"status": "PASS"
},
{
"TestKey": "PT-03",
"status": "FAIL"
}
]
code without this modification (from only 1 csv):
import groovy.json.*
def kindaFile = '''
TestKey;Finished;user;status
Name001;PASS;
Name002;PASS;
'''.trim()
def keys
def testList = []
//parse CSV
kindaFile.splitEachLine( /;/ ){ parts ->
if( !keys )
keys = parts
else{
def test = [:]
parts.eachWithIndex{ val, ix -> test[ keys[ ix ] ] = val }
testList << test
}
}
def builder = new JsonBuilder()
def root = builder {
testExecutionKey 'DEMO-303'
info user: 'admin'
tests testList
}
println JsonOutput.prettyPrint(JsonOutput.toJson(root))
Your sample JSON doesn't match the CSV definition. It looks lile you're using fields [1..-1] from CSV 1, as you stated, but fields [0..-2] from CSV 2. As you only have 2 fields in each CSV that's the equivalent of csv1[1] and csv2[0]. The example below uses [0..-2]. Note that if you always have exactly two fields in your input files then the following code could be simplified a little. I've given a more generic solution that can cope with more fields.
Load both CSV files into lists
File csv1 = new File( 'one.csv')
File csv2 = new File( 'two.csv')
def lines1 = csv1.readLines()
def lines2 = csv2.readLines()
assert lines1.size() <= lines2.size()
Note the assert. That's there as I noticed you have 4 tests in CSV2 but only 3 in CSV1. To allow the code to work with your sample data, it iterates through through CSV1 and adds the matching data from CSV2.
Get the field names
fieldSep = /,[ ]*/
def fieldNames1 = lines1[0].split( fieldSep )
def fieldNames2 = lines1[0].split( fieldSep )
Build the testList collection
def testList = []
lines1[1..-1].eachWithIndex { csv1Line, lineNo ->
def mappedLine = [:]
def fieldsCsv1 = csv1Line.split( fieldSep )
fieldsCsv1[1..-1].eachWithIndex { value, fldNo ->
String name = fieldNames1[ fldNo + 1 ]
mappedLine[ name ] = value
}
def fieldsCsv2 = lines2[lineNo + 1].split( fieldSep )
fieldsCsv2[0..-2].eachWithIndex { value, fldNo ->
String name = fieldNames2[ fldNo ]
mappedLine[ name ] = value
}
testList << mappedLine
}
Parsing
You can now parse the list of maps with your existing code. I've made a change to the way the JSON string is displayed though.
def builder = new JsonBuilder()
def root = builder {
testExecutionKey 'DEMO-303'
info user: 'admin'
tests testList
}
println builder.toPrettyString()
JSON Output
Running the above code, using your CSV1 and CSV 2 data, gives the JSON that you desire.
for CSV1:
testKey,status
Name001,PASS
Name002,PASS
Name003,FAIL
and CSV2:
Kt,Pd
PT-01,Name007
PT-02,Name001
PT-03,Name003
PT-05,Name002
PT-06,Name004
PT-07,Name006
result is:
{
"testExecutionKey": "DEMO-303",
"info": {
"user": "admin"
},
"tests": [
{
"status": "PASS",
"testKey": "PT-01"
},
{
"status": "PASS",
"testKey": "PT-02"
},
{
"status": "FAIL",
"testKey": "PT-03"
}
]
}
but I need exactly the same values for testKey (testKey from CSV1=Kt from CSV2)
{
"testExecutionKey": "DEMO-303",
"info": {
"user": "admin"
},
"tests": [
{
"testKey": "PT-02",
"status": "PASS"
},
{
"testKey": "PT-05",
"status": "PASS"
},
{
"testKey": "PT-03",
"status": "FAIL"
}
]
}

How to set value of Json inside array

I have the following JSON with in an array and when I try to set a value for this JSON, script passes but value isn't set:
{
"langauageCode": "en-US",
"Test": [{
"_modificationTypeCode": "added",
"allocationTypeCode": "3",
"code": "Test1"
}]
}
My code:
def jsonRequest = slurper.parseText(rawRequest)
def builder = new JsonBuilder(jsonRequest)
builder.content.Test.code[0] ='Test2' //Code value is not getting set to 'Test2'
log.info builder.toPrettyString()
Am I not setting the value correctly?
I assume that slurper is an instance of JsonSlurper. If so, there's no need to use JsonBuilder at all, since sluper returns an instance of a Map. So:
import groovy.json.JsonSlurper
import groovy.json.JsonOutput
def req = '''{
"langauageCode": "en-US",
"Test": [{
"_modificationTypeCode": "added",
"allocationTypeCode": "3",
"code": "Test1"
}]
}'''
def slurped = new JsonSlurper().parseText(req)
slurped.Test[0].code = 'Test2'
println JsonOutput.prettyPrint(JsonOutput.toJson(slurped))

Convert RDD to JSON Object

I have an RDD of type RDD[(String, List[String])].
Example:
(FRUIT, List(Apple,Banana,Mango))
(VEGETABLE, List(Potato,Tomato))
I want to convert the above output to json object like below.
{
"categories": [
{
"name": "FRUIT",
"nodes": [
{
"name": "Apple",
"isInTopList": false
},
{
"name": "Banana",
"isInTopList": false
},
{
"name": "Mango",
"isInTopList": false
}
]
},
{
"name": "VEGETABLE",
"nodes": [
{
"name": "POTATO",
"isInTopList": false
},
{
"name": "TOMATO",
"isInTopList": false
},
]
}
]
}
Please suggest the best possible way to do it.
NOTE: "isInTopList": false is always constant and has to be there with every item in the jsonobject.
First I used the following code to reproduce the scenario that you mentioned:
val sampleArray = Array(
("FRUIT", List("Apple", "Banana", "Mango")),
("VEGETABLE", List("Potato", "Tomato")))
val sampleRdd = sc.parallelize(sampleArray)
sampleRdd.foreach(println) // Printing the result
Now, I am using json4s Scala library to convert this RDD into the JSON structure that you requested:
import org.json4s.native.JsonMethods._
import org.json4s.JsonDSL.WithDouble._
val json = "categories" -> sampleRdd.collect().toList.map{
case (name, nodes) =>
("name", name) ~
("nodes", nodes.map{
name => ("name", name)
})
}
println(compact(render(json))) // Printing the rendered JSON
The result is:
{"categories":[{"name":"FRUIT","nodes":[{"name":"Apple"},{"name":"Banana"},{"name":"Mango"}]},{"name":"VEGETABLE","nodes":[{"name":"Potato"},{"name":"Tomato"}]}]}
Since you want a single JSON for you entire RDD, I would start by doing Rdd.collect. Be careful that your set fits in memory, as this will move the data back to the driver.
To get the json, just use a library to traverse your objects. I like Json4s due to its simple internal structure and practical, clean operators. Here is a sample from their website that shows how to traverse nested structures (in particular, lists):
object JsonExample extends App {
import org.json4s._
import org.json4s.JsonDSL._
import org.json4s.jackson.JsonMethods._
case class Winner(id: Long, numbers: List[Int])
case class Lotto(id: Long, winningNumbers: List[Int], winners: List[Winner], drawDate: Option[java.util.Date])
val winners = List(Winner(23, List(2, 45, 34, 23, 3, 5)), Winner(54, List(52, 3, 12, 11, 18, 22)))
val lotto = Lotto(5, List(2, 45, 34, 23, 7, 5, 3), winners, None)
val json =
("lotto" ->
("lotto-id" -> lotto.id) ~
("winning-numbers" -> lotto.winningNumbers) ~
("draw-date" -> lotto.drawDate.map(_.toString)) ~
("winners" ->
lotto.winners.map { w =>
(("winner-id" -> w.id) ~
("numbers" -> w.numbers))}))
println(compact(render(json)))
}

Collect list of attributes with groovy and jsonslurper into single list

What's the best way to collect a list of attributes from a JSON hierarchy? Here's what I'm trying to do:
import groovy.json.JsonSlurper
def jsontxt = '''
{
"lvl1": [
{
"lvl2": [
{
"lvl3": [
{
"a1": false,
"a2": {
"a2b1": false,
"a2b2": false
},
"a3": "wantvalue1"
},
{
"a1": false,
"a2": {
"a2b1": false,
"a2b2": false
},
"a3": "wantvalue2"
}
],
},
],
}
]
}
'''
def jsresult = new JsonSlurper().parseText(jsontxt)
def mytry = jsresult.lvl1.lvl2.lvl3.collect{it.a3} // [[[wantvalue1, wantvalue2]]]
assert ["wantvalue1","wantvalue2"] == mytry
Apologies the input is not as clean as it could be but I didn't want to lose my situation.
What I want is a basic list without the additional empty lists. I know there must be a really cool way to do this but I'm not groovy enough for it. . . help??
Pretty close. Try flatten().
Try jsresult.lvl1.lvl2.lvl3.collect{it.a3}.flatten() or myTry.flatten()