Grimdari Grimdari -5 years ago 143
JSON Question

Append to JSON array with ColdFusion, taking Null values into consideration?

I have been digging here on Stack for some time but I do not believe my brain is getting the hint on how to do this.

I need this JSON:


To look like this JSON:


There are some caveats.

  1. The JSON application I am pulling from, requires these fields be returned, whether null or not: loginHosts, sudoHosts, CPG, mail, loginShell. If they are not returned the application complains.

  2. The JSON application expects loginHosts, sudoHosts, and CPG to be returned as an array [] even if null. I.E.: '{"loginHosts":[],"sudoHosts":[]}'

  3. The original loginHosts and sudoHosts data (such as:, must remain in the JSON string sent back to the application. So all of my new data must be appended.

  4. There are many times when loginHosts and sudoHosts are sent to my application as Null arrays. I.E.: '{"loginHosts":[],"sudoHosts":[]}'

I am developing this on a server running Lucee 4.5 with a Linux OS. To get/set the JSON string; I use CFEXECUTE to ssh to a remote Linux server to pull/push my JSON string. You can see the reason for that here: Populating SELECT with large JSON data set via ColdFusion (Lucee) very slow

The code I have so far:

<cfset user_list = '{"loginHosts":["",""],"sudoHosts":["",""],"CPG":"my_group","mail":"","loginShell":"/bin/bash"}'

<cfset arrayOfStructs = deserializeJson(user_list)>

<cfloop array="#arrayOfStructs#" index="user">
<cfif structKeyExists(user, "loginHosts")>
<cfloop array="#user.loginHosts#" index="login">
<cfset loginHosts = #login#>
<cfif structKeyExists(user, "sudoHosts")>
<cfloop array="#user.sudoHosts#" index="hosts">
<cfset sudoHosts = #hosts#>
<cfif structKeyExists(user, "CPG")>
<cfloop array="#user.CPG#" index="cp">
<cfset CPG = #cp#>
<cfset mail = #user.mail#>
<cfset loginShell = #user.loginShell#>
<cfset my_servers = ",">
<cfset loginHosts = listAppend(loginHosts, "#my_servers#", ",")>
<cfset myStruct = '{"loginHosts":["#loginHosts#"],"sudoHosts":["#sudoHosts#"],"CPG":["#cp#"],"mail":"#mail#","loginShell":"#loginShell#"}'/>

The myStruct is then serialized and sent back to the remote server using the CFEXECUTE method I mentioned earlier.

That code works a little but two things are not working. First, each cfset loginHosts and cfset sudoHosts are only returning the second server in the JSON ( I understand that I am overwriting my own loginHosts in that CFLOOP but I am not sure how to correct that and also check if the loginHosts array is Null.

I am also having problems preserving double quotes so that they remain in the JSON. In my testing I get this:


Notice the missing "," between newserver1 and newserver2?

I have tried adding escaped quotes to my listAppend() like this: """#my_servers""". It seems to work OK until I have a Null value returned in the JSON string.

Any nudge in the right direction would be appreciated!

EDIT ---

Some clarification:

Why: I am simply wanting to add additional server names to a database. To work with that database I am required to use an API that sends JSON and expects JSON back. This has to be done through a BASH shell script that utilizes curl. As I posted above, this is the reason: Populating SELECT with large JSON data set via ColdFusion (Lucee) very slow

I am using serializeJSON() and deserializeJSON() a lot in my application. The "user_list" at the top of my code is simply there to show you what data I am given to work with. It was not written by hand.

EDIT 2 --

My apologies for not adding this line to my code above.

The 'myStruct' at the bottom gets populated with the data from the "arrayOfStructs" and then serialized.

<cfset myJsonvar = serializeJSON(myStruct)/>

The 'myJsonvar' is then sent as a string, over SSH using CFEXECUTE, to a BASH script that then uses curl to submit the data. Yes, convoluted, but its what I have due to security.

Answer Source

I would simplify the code quite a bit. It appears as though all you are doing is adding your two new servers to an existing list of loginHosts within your struct; that doesn't require any looping.

<cfset user_list = '{"loginHosts":["",""],"sudoHosts":["",""],"CPG":"my_group","mail":"","loginShell":"/bin/bash"}'>

<cfset myStruct = deserializeJson(user_list)>

<cfset myServers = ["",""]>

<!--- if loginHosts isn't an array, make it one. --->
<cfif NOT structKeyExists(myStruct, 'loginHosts') OR NOT isArray(myStruct.loginHosts)> 
  <cfset myStruct.loginHosts = []>
<cfset arrayAppend(myStruct.loginHosts, myServers, true)>

<cfset user_list = serializeJSON(myStruct)>
<!--- now user_list is a json string with your new servers added as login hosts, the rest of the struct remains the same. --->
Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download