I've been trying to read an xml file and convert it to json using groovy's JsonBuilder. The problem is that when I print with
def builder = new JsonBuilder(jsonObject)
println builder.toPrettyString()
I got a Caught: java.lang.StackOverflowError
Here is the whole stacktrace
Exception in thread "main" java.lang.StackOverflowError
at groovy.json.JsonOutput.writeObject(JsonOutput.java:259)
at groovy.json.JsonOutput.writeIterator(JsonOutput.java:442)
at groovy.json.JsonOutput.writeObject(JsonOutput.java:272)
at groovy.json.JsonOutput.writeIterator(JsonOutput.java:442)
at groovy.json.JsonOutput.writeObject(JsonOutput.java:272)
at groovy.json.JsonOutput.writeIterator(JsonOutput.java:442)
at groovy.json.JsonOutput.writeObject(JsonOutput.java:272)
at groovy.json.JsonOutput.writeIterator(JsonOutput.java:442)
at groovy.json.JsonOutput.writeObject(JsonOutput.java:272)
at groovy.json.JsonOutput.writeIterator(JsonOutput.java:442)
at groovy.json.JsonOutput.writeObject(JsonOutput.java:272)
at groovy.json.JsonOutput.writeIterator(JsonOutput.java:442)
at groovy.json.JsonOutput.writeObject(JsonOutput.java:272)
at groovy.json.JsonOutput.writeIterator(JsonOutput.java:442)
Here the code.
package firstgroovyproject
import groovy.json.JsonBuilder
class XmlToJsonII {
static void main(def args){
def carRecords = '''
<records>
<car name='HSV Maloo' make='Holden' year='2006'>
<countries>
<country>
Austria
</country>
<country>
Spain
</country>
</countries>
<record type='speed'>Production Pickup Truck with speed of 271kph
</record>
</car>
<car name='P50' make='Peel' year='1962'>
<countries>
<country>
Monaco
</country>
</countries>
<record type='size'>Smallest Street-Legal Car at 99cm wide and 59 kg
in weight</record>
</car>
<car name='Royale' make='Bugatti' year='1931'>
<record type='price'>Most Valuable Car at $15 million</record>
<countries>
<country>
Italia
</country>
</countries>
</car>
<car name='Seat' make='Ibiza' year='1985'>
<record type='price'>barato</record>
<countries>
<country>
Spain
</country>
</countries>
</car>
</records>
'''
def xmlRecords = new XmlSlurper().parseText(carRecords)
def jsonObject = [:]
jsonObject.records = []
def records = jsonObject.records
xmlRecords.car.each {xmlCar ->
records.add([
countries:
xmlCar.countries.children().each{ country ->
println "country : ${country.text()}"
[country: country.text()]
},
])
}
def builder = new JsonBuilder(jsonObject)
println builder.toPrettyString()
//println builder.toString()
}
}
tl;dr: Your second (inner) each
should be a collect
instead.
The real answer: The return value of each
is the original Iterable
that invoked it. In this case that would be the XML object collection defined by the expression xmlCar.countries.children()
. Since the objects in that collection contain references back to their own parents your JSON build causes an infinite regress that results in a stack overflow.
This doesn't happen with your first (outer) use of each
, because you are not using the return value. Instead you are adding to a preexisting list (records
).
By changing the second (inner) each
into a collect
, you are still iterating through the children of the countries
elements. However, instead of returning the original XML children (the result of each
) you are compiling and returning a list of maps of the form [country:"country_string_from_xml"]
, which seems to be the desired behavior.
Confusing each
and collect
is a common rookie mistake in Groovy... which only made it all the more galling when it happened to ME last week. :-)