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!
Related
I'm making a scheduling app, and storing all the scheduled things in firebase with arrays. When I try to schedule something with the same string value, it fails and doesn't add it to the array. I don't know if this is something in swift I can edit, or if it's a firebase setting.
If it's something in swift, here's the code updating the array:
doc.updateData([
"Instructor": FieldValue.arrayUnion(["\(scheduleinstructor)"])
])
If it's something in firebase, could someone please explain a way around this or a simple fix I overlooked?
According to the documentation on adding items to an array:
arrayUnion() adds elements to an array but only elements not already present
So the fact that the duplicate entry is not added is by design. If you want to allow that, you'll have to:
Read the document with the array from the databae.
Extract the array from the document into your application code.
Add the item to the array.
Write the entire modified array back to the database.
I'm using OpenRefine to pull in information on publisher policies using the Sherpa Romeo API (Sherpa Romeo is a site that aggregates publisher policies). I've got that.
Now I need to parse the returned JSON so that those with certain pieces of information remain. The results I'm interested in need to include the following:
'any_website',
'any_repository',
'institutional_repository',
'non_commercial_institutional_repository',
'non_commercial_repository'
These pieces on information all fall under an array called "permitted_oa". For some reason, I can't even work out how to just pull out that array. I've tried writing grel expressions such as
value.parseJson().items.permitted_oa
but it never reutrns anything.
I wish I could share the JSON but it's too big.
I can see a couple of issues here.
Firstly the Sherpa API response items is an array (i.e. a list of things). When you have an array in the JSON, you either have to select a particular item from the array, or you have to explicitly work through the list of things in the array (aka iterate across the array) in your GREL. If you've previously worked with arrays in GREL you'll be familiar with this, but if you haven't
value.parseJson().items[0] -> first item in the array
value.parseJson().items[1] -> second item in the array
value.parseJson().items[2] -> third item in the array etc. etc.
If you know there is only ever going to be a single item in the array then you can safely use value.parseJson().items[0]
However, if you don't know how many items will be in the array and you are interested in them all, you will have to iterate over the array using a GREL control such as "forEach":
forEach(value.parseJson().items, v, v)
is a way of iterating over the array - each time the GREL finds an item in the array, it will assign it to a variable "v" and then you can do a further operation on that value using "v" as you would usually use "value" (see https://docs.openrefine.org/manual/grel#foreache1-v-e2 for an example of using forEach on an array)
Another possibility is to use join on the array. This will join all the things in an array into a string.
value.parseJson().items.join("|")
It looks like the Sherpa JSON uses Arrays liberally so you may find more arrays you have to deal with to get to the values you want.
Secondly, in the JSON you pasted "oa_permitted" isn't directly in the "item" but in another array called "publisher_policy" - so you'll need to navigate that as well. So:
value.parseJson().items[0].publisher_policy[0].permitted_oa[0]
would get you the first permitted_oa object in the first publisher_policy in the first item in the items array. If you wanted to (for example) get a list of locations from the JSON you have pasted you could use:
value.parseJson().items[0].publisher_policy[0].permitted_oa[0].location.location.join("|")
Which will give you a pipe ("|") separated list of locations based on the assumption there is only a single item, single publisher_policy and singe permitted_oa - which is true in the case of the JSON you've supplied here (but might not always be true)
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.
I have a shopping cart array stored in a session:
I give my user the ability to select all items to delete or select individual items via a checkbox.
I am sending the array index via form post to arrayDeleteAt.
Now if I select the bottom 3 items, it does not delete it.
Here is my delete code:
<cfif isDefined("form.leadId") AND listLen(form.leadId)>
<cfloop from="#listLen(form.leadId)#" to="1" step="-1" index="i">
<cfset temp = arrayDeleteAt(session.shoppingcart, #i#)>
</cfloop>
</cfif>
You are going to have more issues with this method of managing your cart. After using ArrayDeleteAt the array's indexes will be recalculated, so when you will most likely delete the wrong item from the array, or you can get an error when you try and delete an item that is out of bounds.
Is see that you are trying to get around this by working backwards through your list, and Dan is right for what your issue is with the code above, but if the list is passed in in an incorrect order then you are in a world of hurt.
I would suggest instead of using an array, use a struct with a surrogate key, such as a UUID, then delete items by that key.
The problem is that you're deleting at the position of the counter, and not the form field that's being passed in. Try this instead:
<cfset temp = arrayDeleteAt(session.shoppingcart, ListGetAt(FORM.leadID, i) />
UPDATE: To get around the issue Tyler mentioned, you can convert your list of indexes from FORM.leadID to an Array using ListToArray and then ArraySort to get them in the order necessary to make sure your deletes are correct.
While my answer does fix your immediate problem, you would definitely be better off following Tyler's advice and using a key for each item in the cart to make sure you're managing the one you actually think you're supposed to be managing :)
In CF 10 or Railo 4, this can be done using the most recent version of the Underscore.cfc library:
<cfscript>
if (structKeyExists(form, 'leadId') && listlen(form.leadId)) {
_ = new Underscore();
variables.shoppingCart = duplicate(session.shoppingCart);
variables.newCart = _.reject(variables.shoppingCart, function(val, index){
return _.include(form.leadId, index);
});
}
</cfscript>
<cfif structKeyExists(variables, "newCart") >
<cflock scope="session" type="exclusive" timeout="10">
<cfset session.shoppingCart = variables.newCart>
</cflock>
</cfif>
You'll see that I'm copying the shopping cart into the variables scope, editing it, then copying it back (with a lock) if necessary. I would've done this all in cfscript if it were possible to write cflocks in cfscript.
(Disclaimer: I wrote Underscore.cfc)
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).