Be careful with returnFormat and JSON
A user pinged me earlier this morning with an interesting issue. His Ajax application wasn't working as expected. He was using jQuery to hit a CFC. Instead of showing you his code, I simplified it a bit which should make the problem a bit simpler to see. Try and figure it out before reading on:
<cffunction name="getTime2" access="remote" returnFormat="json">
<cfset var s = {}>
<cfset s.time = now()>
<cfset s.name = "Current Time">
<cfreturn serializeJSON(s)>
</cffunction>
It's a bit subtle - but the issue is that he mixes both returnFormat and serializeJSON. I've blogged about returnFormat quite a bit. It's one of my favorite features of ColdFusion 8 and it got little press. His use of it here though is not something I normally recommend. You can pass returnFormat as an argument in the request or you can specify it in the method inline as he did. To me - using it inline is a bad idea. Not "bad" bad, but I don't think the CFC should be concerned with its return form. Instead, it should simply handle it's business logic and let the caller ask for a particular format.
Where things go wrong is the serializeJSON. What's happening here is that CF first runs the code of the method (Make a struct, then JSON it), and then it formats the result in JSON. So basically it JSON encodes a string that is already JSON encoded.
If you view this result (*), you can see the result is a bit funky:
"{\"TIME\":\"June, 03 2008 08:20:06\",\"NAME\":\"Current Time\"}"
All those \ should tip you off. If you remove the serializeJSON, you see a result that should look a bit better:
{"TIME":"June, 03 2008 08:20:06","NAME":"Current Time"}
I'd remove both the serializeJSON and the returnFormat. I'd then edit the jQuery code's URL to add returnFormat=JSON.
*Ok, so let me use this blog entry to say once again that 99% of Ajax problems can be solved with a tool like Firebug. Firebug would have let you see the Ajax request and response, and if you were familiar with JSON, it would have been an big clue. It's also a handy way to see times when you forget to turn off CF debugging.
Comments
By the way, please note that "its" is spelled wrongly in the sentence "with it's return form". "it's" means "it is" or "it has", not "of it".
returnFormat=json&queryFormat=column
I use that when I want to "tell the CFC to do it via an URL" but how can I do the same thing directly on the CFC file?
<cffunction name="foo" returnFormat="json" queryFormat="column">
Is not good :(
This is a Spry JSONDataset.
if (req is JSONP)
arguments.returnformat='json'
else
arguments.returnformat='wddx'
It feels a bit weird that you can send it as an argument but not treat it as one.
if (code executed indicates json return)
url.returnFormat="JSON";
else
url.returnFormat="plain";
That will make my life easier. Sometimes it is simply easier to decide on the server what the returnformat should be, but only after you see the request and it's result. For instance if you have the same methods being accessed by both JSONRPC and JSONP.
Blog it if you wish, I have no blog. But it works for sure.

