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:

<cfcomponent output="false">

    <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:

<cfcomponent name="general" extends="coldbox.system.eventhandler" output="false">

    <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" src="/scripts/lib/jquery-1.3.2.min.js"></script>

<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()

{"dottest1":"dot1","dottest2":"dot2","dottest3":"dot3","arraytest1":"array1","arraytest2":"array2","arraytest3"
:"array3","mytest1":"value1","mytest2":"value2","mytest3":"value3"}

ColdFusion 8 and returnFormat="json"

{"DOTTEST1":"dot1","DOTTEST2":"dot2","DOTTEST3":"dot3","arraytest1":"array1","ARRAYTEST2":"array2","arrayTest3"
:"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?

Related Blog Entries

Comments
Russ's Gravatar I think it stinks. I can't think of a reason why the case should change to begin with. I've hit this a few times while debugging and it is irritating.
# Posted By Russ | 4/24/09 3:20 AM
Francois Levesque's Gravatar @Russ,

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...
# Posted By Francois Levesque | 4/24/09 10:38 AM
oscar arevalo's Gravatar Well, in the case of the pure CF test, it at least is consistent with how CF manages keys in other circumstances. For example you would have exactly the same result when creating a plain struct using mystruct.someKey and mystruct["someKey"]; if you do <cfloop collection="mystruct">, you would get an uppercase key name on the first example and the correct key name on the second one. Same thing happens when creating XML nodes programmatically. Yes, it sucks, but at least is reliable in **that** aspect. Basically, from what I've seen, CF will only respect the correct case whenever the name has been given between quotes, otherwise it will most likely end up as all uppercase.

I wonder how Railo and BD handle the same situations...
# Posted By oscar arevalo | 4/26/09 4:37 AM
Francois Levesque's Gravatar @Oscar,

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.
# Posted By Francois Levesque | 4/27/09 10:19 AM
Sami Hoda's Gravatar If CF8 is consistently inconsistent, have you reported this as a bug to the CF team?
# Posted By Sami Hoda | 4/27/09 8:19 PM
oscar arevalo's Gravatar @Sami,
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.
# Posted By oscar arevalo | 4/27/09 8:29 PM
William B's Gravatar I've always held that the use of the CF Ajax components are primarily for rapid application prototyping. They are fantastic for putting up some very basic functionality, but when you need more advanced configuration it is then time to dig in, and go straight to Ext JS itself. Mike's question is a prime example of this. We have nearly no control over a cfgrid's configuration prior to render, and the configuration options that we do have (through the cfgrid and cfgridcolumn attributes) barely scratch the surface to what one can do with an Ext Grid. The PagingToolbar is a class object of Ext and, as such, you should be able to add it to any toolbar of a grid: top, bottom, or both.
# Posted By William B | 6/22/09 4:31 PM
BlogCFC was created by Raymond Camden. This blog is running version 5.9.3.000. Contact Blog Owner