JSON Serialization And Case... ColdBox vs ColdFusion 8
Earlier today I blogged about being aware of the differences between the way ColdBox and ColdFusion 8 handle variable names when serializing to JSON for remote calls (the original post name is much more elegant, I promise). Several questions arose as to the different ways variables can be created and how exactly each method would output, so I decided to put together this short test.
The test is rather simple. I create a structure with 9 keys: 3 using in-line declaration (<cfset struct = { key = value } />); 3 using dot-notation (<cfset struct.key = value />); 3 using array notation (<cfset struct[ "key" ] = value />). The difference between the 3 keys within each sub-set is that one key's name is all lowercase, another is all uppercase and the last uses camelCase.
Setting up the ColdFusion 8 test
After creating my new ColdBox application I setup a new file in the root of the project called cf-remote.cfc. It's pretty simple, so I'll just dump it right here.
cf-remote.cfc contents:
<cffunction name="cf_testRemote" access="remote" returntype="struct">
<cfset var result = { mytest1="value1", MYTEST2="value2", myTest3="value3" } />
<cfset result.dottest1 = "dot1" />
<cfset result.DOTTEST2 = "dot2" />
<cfset result.dotTest3 = "dot3" />
<cfset result[ "arraytest1" ] = "array1" />
<cfset result[ "ARRAYTEST2" ] = "array2" />
<cfset result[ "arrayTest3" ] = "array3" />
<cfreturn result />
</cffunction>
</cfcomponent>
We're setting up a simple service with a single method. We allow access from remote requests, and we declare we'll be returning a struct. The rest is just variable declaration to fit with our test and a return to caller.
Setting up the ColdBox test
Every remote call in a ColdBox app should pass by coldboxproxy.cfc. This proxy then turns around and asks ColdBox to execute the requested event. Our event looks pretty much the same as our CF8-only test, so here it is with details right after.
handlers/general.cfc contents:
<cffunction name="init" access="public" returntype="general" output="false">
<cfargument name="controller" type="any">
<cfset super.init(arguments.controller)>
<!--- Any constructor code here --->
<cfreturn this>
</cffunction>
<cffunction name="cb_testRemote" access="public" returntype="void">
<cfargument name="event" type="any" required="true" />
<cfset var result = { mytest1="value1", MYTEST2="value2", myTest3="value3" } />
<cfset result.dottest1 = "dot1" />
<cfset result.DOTTEST2 = "dot2" />
<cfset result.dotTest3 = "dot3" />
<cfset result[ "arraytest1" ] = "array1" />
<cfset result[ "ARRAYTEST2" ] = "array2" />
<cfset result[ "arrayTest3" ] = "array3" />
<cfset event.renderData( type="json", data=result ) />
</cffunction>
</cfcomponent>
There are two noticeable differences between the two tests. Even if we know that a ColdBox event will only be called by remote requests, we still set it's access to "public". The reason for this is that this method is actually called by the proxy, and not by the calling page. The other difference is the last line in the method. Rather than use <cfreturn />, we're using ColdBox's event.renderData() method. Events aren't made to return data explicitly, so even if we'd used <cfreturn /> the value wouldn't make it back to the caller. That's why we need to use this method from within ColdBox. What this does is serialize the variable provided into a JSON string so it can be returned to the calling page.
Viewing the results
All that's left now is executing our test. I told you this was a simple test, so here's the entirety of the client code.
The actual test:
<script type="text/javascript">
<cfwddx action="cfml2js" input="#getSetting( 'AppMapping' )#" toplevelvariable="APPMAPPING" />
$().ready( function () {
var path = "/" + APPMAPPING + "/";
$.getJSON( path + "coldboxproxy.cfc?method=process&event=general.cb_testRemote&returnFormat=plain", {}, function () {} );
$.getJSON( path + "cf-remote.cfc?method=cf_testRemote&returnFormat=json", {}, function () {} );
});
</script>
Using jQuery's $.getJSON() method we can easily ping both our methods and view the result. A quick rundown of what's happening in each AJAX call:
- coldboxproxy.cfc
- All my remote calls pass by coldboxproxy's process method. We then specify which event to execute (handler.action) and in which format to return it. We're using returnFormat="plain" here because we don't want ColdFusion to re-serialize the already JSON'ified string.
- cf-remote.cfc
- Pretty simple call. We ping cf-remote.cfc, and request the cf_testRemote method. We then ask ColdFusion to convert our result to a JSON string.
The results of all this
We didn't do all of this for nothing. Let's compare the results, as shown by Firebug.
ColdBox and event.renderData()
:"array3","mytest1":"value1","mytest2":"value2","mytest3":"value3"}
ColdFusion 8 and returnFormat="json"
:"array3","MYTEST1":"value1","MYTEST2":"value2","MYTEST3":"value3"}
Every single key, in ColdBox, is in lowercase. In ColdFusion 8, however, they're all uppercase... or are they? Notice the array_ tests. Keys named with array notation preserved their case. All other declarations were converted.
Personally, I like the fact that either solution converts to a specific case. Our JavaScript methods shouldn't have to care how a variable name was typed. It should be able to depend on the fact that key names are always lower- or upper- case. IMO, though, the fact that CF8 doesn't convert keys that were named through array notation to upper case makes it unreliable. Sure, it's always the same... As a CF developer, we know how we named our variables and we know what to expect. What happens, however, when another programmer is responsible for the JavaScript side? Does he constantly have to validate with the CF guy how he typed his variables? I may be nitpicking here, and it is getting kind of late, so I'll leave it to you. What do you think?


I agree with you. It's hard to try and guess what's going to happen to our variable names. And when we hit a bug, we're not used to checking for the case of our variable names. That's why I did these tests, so we know once-and-for-all what's really going on.
However, Luis was talking about _maybe_ adding in a caseless serializer, that would respect original case. If he could get that to work, ColdBox would be able to appeal to all crowds :).
http://blog.critical-web.com/blog/index.cfm/2009/4...
I wonder how Railo and BD handle the same situations...
I agree that it's consistent, at least in some way. I'm just concerned, at least when sending data over to JS, you have to think twice about whether your key names will be uppercase or not.
I'd also be interested in knowing how other engines handle this. I'll try and get some help on the matter and report back.
Well, I think it makes sense if you think about it. CF is case insensitive when it comes to tags, language functions and variable naming; from CF's perspective naming a variable myVar and myvar are exactly the same, so why would somestruct.someKey and somestruct.SOMEkey would be handled differently? Now, in the case in which you are giving the name in quotes you are being explicit about its string representation and CF will respect that.
I don't see this as a bug, but more as a side effect of the language being case insensitive.