Issue
Working on a Jenkins pipeline, I observed what looks like infinite recursion causing a stack overflow when I use JsonOutput.toJson()
on a net.sf.json.JSONObject
that slurped a JSON string containing null.
The following minimal code demonstrates the problem:
// Jenkinsfile
@Library('libs@dev') libs
import groovy.json.JsonOutput
pipeline {
agent any
stages {
stage( "json" ) {
steps {
script {
my_lib.to_json_handbuilt_linkedhashmap()
my_lib.to_json_readjson()
my_lib.to_json_readjson_as_linkedhashmap()
}
}
}
}
}
// vars/my_lib.groovy
import groovy.json.JsonOutput
def asMap(j) {
return j as LinkedHashMap
}
// This function is successful.
def to_json_handbuilt_linkedhashmap() {
def d = [:]
d.issues = null
echo "---- handmade LinkedHashMap ----"
echo "d ${d}"
echo "d.getClass() ${d.getClass()}"
echo "JsonOutput.toJson(d) ${JsonOutput.toJson(d)}"
}
// This function fails from infinite recursion causing a stack overflow.
def to_json_readjson() {
def d = readJSON(text: '{ "issues" : null }')
echo "---- readJSON ----"
echo "d ${d}"
echo "d.getClass() ${d.getClass()}"
echo "JsonOutput.toJson(d) ${JsonOutput.toJson(d)}"
}
// This function also fails from infinite recursion causing a stack overflow.
def to_json_readjson_as_linkedhashmap() {
def d = asMap(readJSON(text: '{ "issues" : null }'))
echo "---- readJSON -> asMap ----"
echo "d ${d}"
echo "d.getClass() ${d.getClass()}"
echo "JsonOutput.toJson(d) ${JsonOutput.toJson(d)}"
}
In the code above, to_json_readjson()
fails with a stack overflow when JsonOutput.toJson()
is called with the net.sf.json.JSONObject
returned by readJSON(text: '{ "issues" : null }')
.
The Jenkins console output is at the end of this post.
In to_json_handbuilt_linkedhashmap()
JsonOutput.toJson()
is successful when called with a handcrafted LinkedHashMap
equivalent to { "issues" : null }
.
Lastly, in to_json_readjson_as_linkedhashmap()
, JsonOutput.toJson()
again fails with a stack overflow when called with a LinkedHashMap
created from a net.sf.json.JSONObject
.
Question:
Can someone please explain what's causing the stack overflow when readJSON()
and/or JsonOutput.toJson()
are used with a JSON string that has null
?
Because my handcrafted LinkedHashMap
was successful with JsonOutput.toJson()
, I thought the problem was passing JsonOutput.toJson()
a net.sf.json.JSONObject
.
But I think that theory is ruled out because in to_json_readjson_as_linkedhashmap()
, I give JsonOutput.toJson()
a LinkedHashMap
, albeit created from a net.sf.json.JSONObject
.
The problem would appear to be some combination of readJSON()
and/or JsonOutput.toJson()
that I'm failing to grasp.
I tried, but have given up trying to use a JsonSlurper
, because I'm unable to even create an instance of one.
The (truncated) stack overflow error likely showing infinite recursion:
Posting build status of FAILED to bitbucket.company.comjava.lang.StackOverflowError
at java.io.PrintStream.flush(PrintStream.java:338)
at sun.nio.cs.StreamEncoder.implFlush(StreamEncoder.java:297)
at sun.nio.cs.StreamEncoder.flush(StreamEncoder.java:141)
at java.io.OutputStreamWriter.flush(OutputStreamWriter.java:229)
at java.util.logging.StreamHandler.flush(StreamHandler.java:259)
at java.util.logging.ConsoleHandler.publish(ConsoleHandler.java:117)
at java.util.logging.Logger.log(Logger.java:738)
at java.util.logging.Logger.doLog(Logger.java:765)
at java.util.logging.Logger.throwing(Logger.java:1447)
at org.codehaus.groovy.runtime.DefaultGroovyMethods.getProperties(DefaultGroovyMethods.java:391)
at groovy.json.JsonOutput.getObjectProperties(JsonOutput.java:327)
at groovy.json.JsonOutput.writeObject(JsonOutput.java:320)
at groovy.json.JsonOutput.writeMap(JsonOutput.java:458)
at groovy.json.JsonOutput.writeObject(JsonOutput.java:321)
at groovy.json.JsonOutput.writeMap(JsonOutput.java:458)
at groovy.json.JsonOutput.writeObject(JsonOutput.java:321)
at groovy.json.JsonOutput.writeMap(JsonOutput.java:458)
at groovy.json.JsonOutput.writeObject(JsonOutput.java:321)
at groovy.json.JsonOutput.writeMap(JsonOutput.java:458)
at groovy.json.JsonOutput.writeObject(JsonOutput.java:321)
at groovy.json.JsonOutput.writeMap(JsonOutput.java:458)
at groovy.json.JsonOutput.writeObject(JsonOutput.java:321)
at groovy.json.JsonOutput.writeMap(JsonOutput.java:458)
at groovy.json.JsonOutput.writeObject(JsonOutput.java:321)
Solution
Can you sidestep this immediate problem by using readJSON
's returnPojo: true
parameter, thereby solving your overall task sooner?
Getting plain old null
s rather than net.sf.json.JSONNull objects really helped me today, though my problem involved producing CSV rather than using JsonOutput.
Answered By - BillDMoose
Answer Checked By - David Marino (JavaFixing Volunteer)