This is a follow up question to: Append to JSON array with ColdFusion, taking Null values into consideration?
That question was answered yesterday and worked perfectly (Thank you Kevin B. and Leigh!). However, the application I am pulling my JSON data from threw me a curve ball this morning. Sometimes, depending on the data I am requesting, it returns the entire JSON as an array like this:
[
{
"loginHosts": [
"server1.example.com"
],
"sudoHosts": [
"server1.example.com"
],
"CPG": [
"my_group"
],
"mail": "myuser#example.com",
"loginShell": "/bin/bash"
}
]
I don't know why that application does this. If I knew this was a possibility I would have added that information to my previous question, my apologies.
My attempts to find a solution lead me here first: Using JSON Data with Coldfusion . Looping over the JSON array as a collection seemed to work, but only if none of the array values were Null. I thought using this code, as in the previous question, would work if I used it for all the JSON fields:
<cfif NOT structKeyExists(myStruct, 'sudoHosts') OR NOT isArray(myStruct.sudoHosts)>
<cfset myStruct.sudoHosts = []>
</cfif>
This was not the case. I continually get:
Error: Can't cast Complex Object Type Array to String
Looking through the debug output, Lucee did throw this out: string Use Built-In-Function "serialize(Array):String" to create a String from Array. I did more digging and found this article: Railo tip: store complex data by using serialize(data). Sadly, Null values have struck again. Also, my understanding is serialize() is similar to evaluate(), and not recommended.
I will continue looking for a solution but any help is, as always, greatly appreciated!
-- EDIT --
I came across this thread: ColdFusion JSON object vs array of objects. I noticed the JSON in the question is an ARRAY [], and I applied the answer to my code, but am still running into the Null problem. I guess I don't know how to check for nested Null values. :(
Take it one step at a time.
Ideally you should determine why the response differs. Since you say those differences usually correspond to something different in your request, that strongly suggests you may be overlooking (or possibly misunderstanding) something in the remote API. I would recommend re-reviewing the API to identify that "something", in order to figure out the right approach. Otherwise, the code will quickly become unmanageable and inefficient as you continue to tweak it to handle each "new" situation.
If for some reason the API truly is returning different results without a valid reason, the best you can do is to code according to what you expect and fail gracefully when you receive something else. Start by listing the expected possibilities:
Response is a single structure containing certain keys OR
Response is an Array of structures containing certain keys
Based on the above, you can use the IsArray and IsStruct functions to determine the format of the response, and handle it accordingly. First examine the deserialized object. If it is an array, extract the structure in the first element (Note, I am assuming the array only contains a single element, as in the example. If it can contain multiple elements, you will need additional handling).
<cfset data = deserializeJson(originalJSON)>
....
<!--- Extract structure from first element of array --->
<cfif IsArray(data) && arrayLen(data)>
<cfset data = data[1]>
</cfif>
Next verify you are now working with a structure, containing the expected key(s). If so, go ahead with your usual processing. Otherwise, something unexpected happened and the code should perform the appropriate error handling.
<!--- Verify object is a structure and contains expected key(s) --->
<cfif IsStruct(data) && structKeyExists(data, "loginHosts")>
... process data as usual
<cfelse>
... data is not in expected format, do error handling here
</cfif>
The above is a very quick and dirty example, but should demonstrate the basic idea. As long as you are certain you are using the API correctly, all you can do is code for the expected and fail gracefully when something different happens.
Related
I have some problems mapping an array of objects in JS.
This is how the response looks in my console:
[{...}]
...and when I expand it I get this:
0:{id:0, document:{...}}
1:{id:1, document:{...}}
Usually the response I get is always without this number in front of each object, like this:
{id:0, document:{...}
{id:1, document:{...}
I tried every approach I know and I cant't manage to handle it.
The goal is to take each value out of "document" property and dynamically display it in some kind of table.
This is the way the browser devtools decides to display the array, but it is actually correct, if I'm not mistaken you're still dealing with an array.
You can verify this by logging the following:
(Replace myVar with the variable name you chose for your response array)
console.log(Array.isArray(myVar))
If it outputs true then you're fine and you are dealing with an array.
I'm receiving a response from a server-call, which I've decoded from JSON into a List
Now any of the level 1 list-values I can access, no issues. However, some of the values I'm receiving are complex, instead of a simple data-type like a String or an int, it is a Map object, with nested variables inside.
How do I get access to those?
I can't create a class to parse the Map objects into, because they could be of many different kinds, and there's no way for me to understand what they will be in advance.
Examples of a server response I could receive:
[{
info1: something,
info2: something else,
info3: something something else,
info4: [{
object1: data,
object2: data2,
object3: data3
}],
info5: something something something else,
}]
At the same time, I cannot know in advance whether or not I will be receiving a response at any given point that will contain such a map object, or which kind it will be. So how do I get access to those variables? Right now, if I type
print(list[0][info4]);
I would get
[{object1: data, object2: data2, object3: data3}]
From there, what I need to be able to do, is get the individual variable data, e.g. for object1 inside info4
data
So I have the data, but after getting the individual index, how to I access the data inside that object? What notation do I use? Everything I've tried either returns null, or type errors.
Also, the line of code I'm using to decode the server response is as follows.
List<dynamic> data2 = jsonDecode(response.body);
So the data is currently in the format of List and if I need to use some other line to decode it into a different format, or something, that might give you an idea where I'm starting from.
Aha! Managed to figure it out. It's actually because of the brackets, and bracket-types.
Effectively, the [ bracket denotes a List, whereas the { bracket denotes a Map. I didn't understand this going in, so the notations I was using were confusing.
Now that I understand that, The notation works.
So in the above example, the method I would use to get access the to object1: data result, would be as follows.
print(info[0][info4][0][object1]);
So in essence, due to the [] brackets, it's asking for an integer case to identify a given object, whereas the {} brackets are asking for a String case to identify to object instead. Once I understand this, the rest came automatically, and I was able to resolve the parsing of the data as needed.
Thank you for your insightful comments though!
Apologies for the basic question but I'm new to regular expressions and am really struggling to find a solution to the problem I am facing.
I am trying to pull out a particular field from a json response dynamically, which can change each time I call it.
The response is:
[{"colorPartNumber":"10045112022164298","skuPartNumber":"0400218072057","productColor":{"identifier":"Dark blue","label":"Dark blue","hex":"#0000A0"},"productSize":{"identifier":"0","label":"0","name":"Designer","scaleLabel":"apparel-wmn","schema":{"name":"UK","labels":["8"]}},"soldOut":true,"onlyOneLeft":false,"limitedAvailability":false,"preorder":false,"comingSoon":false,"visible":true,"displayable":true,"buyable":false,"availableInPhysicalStore":false,"expectedShippingDate":null},{"colorPartNumber":"10045112022164298","skuPartNumber":"0400094632819","productColor":{"identifier":"Dark blue","label":"Dark blue","hex":"#0000A0"},"productSize":{"identifier":"1","label":"1","name":"Designer","scaleLabel":"apparel-wmn","schema":{"name":"UK","labels":["10"]}},"soldOut":true,"onlyOneLeft":false,"limitedAvailability":false,"preorder":false,"comingSoon":false,"visible":true,"displayable":true,"buyable":false,"availableInPhysicalStore":false,"expectedShippingDate":null},{"colorPartNumber":"10045112022164298","skuPartNumber":"0400218072040","productColor":{"identifier":"Dark blue","label":"Dark blue","hex":"#0000A0"},"productSize":{"identifier":"2","label":"2","name":"Designer","scaleLabel":"apparel-wmn","schema":{"name":"UK","labels":["12"]}},"soldOut":true,"onlyOneLeft":false,"limitedAvailability":false,"preorder":false,"comingSoon":false,"visible":true,"displayable":true,"buyable":false,"availableInPhysicalStore":false,"expectedShippingDate":null},{"colorPartNumber":"10045112022164298","skuPartNumber":"0400468014814","productColor":{"identifier":"Dark blue","label":"Dark blue","hex":"#0000A0"},"productSize":{"identifier":"3","label":"3","name":"Designer","scaleLabel":"apparel-wmn","schema":{"name":"UK","labels":["14"]}},"soldOut":false,"onlyOneLeft":true,"limitedAvailability":false,"preorder":false,"comingSoon":false,"visible":true,"displayable":true,"buyable":true,"availableInPhysicalStore":false,"expectedShippingDate":null}]
I am trying to pull out the skuPartNumber, but only when the "buyable" value is set to true.
Every thing I try I cannot seem to get just this one value :(
So in the example above the only value I want to pull out is 0400468014814.
This json is dynamic so there could be 100 values coming back, but the principle is the same.
One example of a failed attempt is skuPartNumber(.*?)"buyable":true, which only gives me the very first value (0400218072057), which is wrong.
Once again sorry for the basic question.
Try the following regular expression:
\"skuPartNumber\":\"(\d+)\"(?:[^}]*?\}){3}[^}]*?\"buyable\":true
Your answer is in the first match group.
Here is Google calendar data retrieved from https://www.googleapis.com/calendar/v3/users/me/calendarList.
The response is in JSON, so I used the ColdFusion method deserializeJson to turn it into a CFML structure.
I cannot figure out the syntax to loop over this data. You'll see that within the CFML struct, 'items' is an array of calendars returned from Google, and every attempt to access the array within the struct generates a Java string conversion error or a syntax error, likely because I can't get the syntax correct.
Any help is very appreciated, thanks very much
Some trial and error will help - you are already on your way. The root object you dumped out already but we'll do it here to represent the object you dumped out as "obj":
<cfset obj = deserializejson(googleCal)>
The first level is a struct so you would address them as follows:
#obj.etag# // this is a string
items contains an array. So you have items[1-n] ... however many are in the array. This code would set myArrayObj as an array with 3 items (based on the dump above).
<cfset myArrayObj = obj.items>
You can verify using isArray:
#isArray(myArrayObj)#
Each array member contains a struct in turn so you can output:
#myArrayObj[1].accessRole# // this would be a string
... And so on. Make a note that index item 3 in the array is a blank struct so you need to "check" to see if the struct is empty before you work with it's keys. Investigate "structKeyExists()" for that purpose.
If you want to deal with each of the "items" in a loop (a pretty typical choice) you would simply do a cfscript loop or cfloop using array as in:
<cfloop array="#myArrayObj#" index="i">
<cfif structKeyExists(myArrayOb[i], "accessRole")>
#myArrayObj[i].accessRole#
</cfif>
</cfloop>
Hope this helps. Good luck!
It seems like this should be relatively simple, but apparently not so much. I can't figure out for the life of me how to store strings and integers in an array in GWT. What data type do you use? If I use JsArrayString, it throws an IllegalArgumentException when retrieving an index containing a number. I obviously can't use JsArrayInteger (I have strings). JsArray requires a type, of which I have no idea what to use (if it can be used), I've tried String but get the same results.
The data being retrieved is from a script page and it does not have the ability to distinguish between strings and ints (ColdFusion).
Now this is possible using JsArrayMixed.
I think you can use Object as your type. You'll have to box your primitives (int) in the Integer class if it doesn't do it automatically for you. You will also have to do some type checking after you grab what's in the array.
It's kinda clunky, but you could always do some brute-force determinations of whether a given value is an int or string:
<cfset val1="one">
<cfset val2="1">
<cfif int(val(val1)) eq val1>int<cfelse>string</cfif>
<br />
<cfif int(val(val2)) eq val2>int<cfelse>string</cfif>
This should give you
string
int
GWT does not currently have the functionality to parse an array structure that contains two different types of data unless in strict JSON form (not as part of a JSON structure in an array passed from ColdFusion apparently).