A reader sent in an interesting question today. He was trying to make use of jQuery to post an array of data to a ColdFusion component. His CFC was expecting, and demanding, an array argument. Whenever he fired off the request though he received an error from ColdFusion saying the argument was not a valid array. Let's look at an example of this so it is more clear.
I'll start off with my server side component. It handles the incredibly complex task of returning the size of an array.
<cfcomponent>
<cffunction name="handleArray" access="remote" returnType="numeric">
<cfargument name="data" type="array" required="true">
<cfreturn arrayLen(arguments.data)>
</cffunction>
</cfcomponent>
On the client side, I'm going to use jQuery to post a static query to the CFC. This will happen immediately on document load so I don't have to bother clicking a button or anything fancy like that. (Note, in the code block below I've removed the html, head, and body tags since they are all empty. I do have a script block to load in the jQuery library though.)
$(document).ready(function() {
var mydata = [1,2,3,4,5,"Camden,Raymond"];
$.post("test.cfc", {method:"handleArray",data:mydata, returnFormat:"plain"}, function(res) {
alert($.trim(res));
})
})
So, what would you expect to happen here? Well first off, it is important to remember that we are talking about an HTTP post here. You cannot send a complex data type of POST. It must be encoded somehow. jQuery does indeed encode the data - just not how you would expect it. Upon running this and examining the POST data in my Chrome dev tools, I see the following:

Not what you expected, right? You can see why ColdFusion complained. The "Data" argument doesn't exist. Instead we have a lit of things named like Data, but not quite. You can try using toString on the array, but that doesn't correctly handle the comma in the data. So what to do?
What I recommended was converting the array to JSON. It always surprises me when I remember that jQuery can't produce JSON on its own, but there are plugins out there that will do it it for you. Because JSON is a string format though I thought I'd write up a quick function to generate the string for me. This function makes the assumption that are array only contains simple values of numbers and strings.
$(document).ready(function() {
var mydata = [1,2,3,4,5,"Camden,Raymond"];
var myds = serialize(mydata)
$.post("test.cfc", {method:"handleArray",data:mydata, returnFormat:"plain"}, function(res) {
alert($.trim(res));
})
function serialize(arr) {
var s = "[";
for(var i=0; i<arr.length; i++) {
if(typeof(arr[i]) == "string") s += '"' + arr[i] + '"'
else s += arr[i]
if(i+1 < arr.length) s += ","
}
s += "]"
return s
}
})
As you can see, I wrote a serialize function to handle converting the array into a JSON-encoded array. This isn't the only change though. We still aren't sending an array to the CFC. It's a string. So I rewrote the CFC to handle it a bit better:
<cfcomponent>
<cffunction name="handleArray" access="remote" returnType="numeric">
<cfargument name="data" type="any" required="true">
<cfif isJSON(arguments.data)>
<cfset arguments.data = deserializeJSON(arguments.data)>
</cfif>
<cfreturn arrayLen(arguments.data)>
</cffunction>
</cfcomponent>
I normally don't like to "muck" up my code so that it has outside knowledge like this. However, I'd probably have a remote service component in front of this component anyway and the whole issue would become moot.
There are probably many better ways of handling this. Any suggestions?
Archived Comments
Depending on what browser's you are trying to support, you could utilize native JSON parsing, which I believe would be easier and faster.
Wouldn't jQuery's serializeArray() method work for this?
I do not know if it would help in this situation, but Javascript does have a .toSource() method which will return the data from an object.
@Garret: You misunderstand. I'm not parsing. I'm _creating_ json.
@Todd: Nope, try it - it doesn't return something valid.
@Robert: Interesting, what does toSource return for my array?
@ray: oops... I meant native JSON serializing. :-) In this case JSON.stringify should do the trick no? (leaving out support for old browsers of course).
I must admin - that is new to me. FF 3.5, Chrome, and IE8 only, right? Safari I assume?
Probably not the best way, but I had a similar issue and did a replace in the CFC to eliminate the extra info from jQuery and build it into a list. I then loop it on the CFC side.
Hmm got spammed trying to post a cross browser concept....
Ideally you could check and see if the JSON property is defined within the window object, if not utilize jQuery's $.getScript to load the json2 lib by Douglas Crockford, otherwise it will use the native one (faster).
But ie8, ff3.5, and webkit support it natively.
@Ray:
That comes back with a JSON representation of the object. I haven't had a chance to check it though
Man, it just goes to show you - I need to review a good JS Reference one day. I have no idea what's in the language now. I've got an ORA ref from like 5 years ago - something tells me it is a bit out of date now. ;)
Man the world needs jCFC back!
$.cfcp( "dotpath.to.cfc", "handleArray", { data: arr }, function( r ){ /* Use r immedaitely */ } );
And done.
Just to be picky - you aren't using r "immediately" - you are using it when the result of the call returns. I'm only being anal here because I know a lot of CFers coming to Ajax for the first time have a hard time with asynchronous behavior.
I find myself using synchronous calls more often than async ones. I have gotten in trouble more than once expecting that an async call has already fired and it has not (I'm staring at you $.getJSON())..
I haven't seen the $.cfcp project and it doesn't look like I will ever see it, but I am comfortable with calling things the old-fashioned way.
I literally tried to respond to this post 6 times just now but every combination of what I wanted to say was flagged as spam. Have I offended the jedi?
Sorry man - the spam protection can be a bit mysterious at times.
@Robert - switch back to async and put any logic that requires a response inside the "success" callback. Your users will thank you for it.
Sorry for reviving an old post, but I was looking for a better way to deal with this, and I thought I should share what I found.
By looking at the HTTP calls made when using cfajaxproxy, I discovered that you can send a single argumentCollection parameter as a JSON string to call the remote CFC method.
So the client side call looks something like this (using jquery-json plugin to do the serialization):
var params = {data: ['a', 'b', 'c']};
$.post('test.cfc', {method:"handleArray", returnFormat:"plain", argumentCollection: $.toJSON(params)}, function(res) {
alert($.trim(res));
});
The benefit here is that there's no change to the CFC.
Well now that is darn interesting. I hadn't though of using argumentCollection via Ajax. Would you be opposed to me blogging on that later? (In a more general context I mean.)
Not at all, please do.
Also, I added this (and my solution) as a question on stack-overflow if anyone has any more thoughts:
http://stackoverflow.com/qu...
Dude, thank you again: http://www.coldfusionjedi.c...