Maintaining Key-Case During JSON Serialization In ColdFusion
ColdFusion is not a case-sensitive programming language. Many other languages are case-sensitive. This can occasionally cause a problem when we pass data from ColdFusion into another context. In the world of Web 2.0 and AJAX-driven applications, this friction can quickly be felt during the creation of JSON (Javascript Object Notation) data; when we serialize our ColdFusion structs into JSON, ColdFusion tends to convert all struct keys to UPPERCASE. In order to maintain key-case during the JSON serialization lifecycle, we have to define our keys using array-notation rather than dot-notation.
This array-notation trick has been around for a long time; but yesterday, when I was setting up a ColdFusion API framework for a remote JSON-RPC client, I discovered that once you define a key using array-notation, you can then later redefine that key using dot-notation without losing the original case. This is pretty exciting since dot-notation is much more friendly for both reading and writing.
To demonstrate this behavior, I have put together a sort of mock JSON-RPC request-response lifecycle. In the following code, notice that I am defining struct keys using both dot-notation (as a control) and array-notation. For the last key, however, be sure to notice that I am, several times, redefining it using dot-notation.
<!---
Create the struct that we are going to serialize into JSON
for our API response. Notice that we are defining the struct
but NOT populating it at this point so we can maintain case
in the JSON result.
--->
<cfset response = {} />
<!---
Set the JSON-RPC version. In this key, I'll just use the standard
dot-notation to demonstrate the default behavior of keys during
the JSON serialization lifecycle.
--->
<cfset response.jsonRPC = "2.0" />
<!---
Now, let's define our error key. Notice that we are using
array-notation rather than dot-notation in order to set the key.
This way, when we serialize the struct into JSON, the key will
maintain its oringal case.
--->
<cfset response[ "error" ] = {} />
<!---
Now, we are going to define our result key. This will be done
using the array-notation as well, to maintain key-case.
--->
<cfset response[ "result" ] = "" />
<!--- ----------------------------------------------------- --->
<!--- ----------------------------------------------------- --->
<!---
Once we have defined the "result" key using array-notation,
we can then redefine the key using dot-notation without
sacrificing the original case.
--->
<cfset response.result = "foo" />
<cfset response.RESULT = "blam" />
<cfset response.ReSuLt = "bar" />
<cfset response.RESult = [ "Joanna", "Sarah", "Tricia" ] />
<!--- ----------------------------------------------------- --->
<!--- ----------------------------------------------------- --->
<!---
Serialize the struct into JSON format for our JSON-RPC
response.
--->
<cfset serializedResponse = serializeJSON( response ) />
<!---
Stream the value back to client.
NOTE: We are using text/plain here for debugging purposes;
typically, JSON would be sent back as application/json.
--->
<cfcontent
type="text/plain"
variable="#toBinary( toBase64( serializedResponse ) )#"
/>
When we run the above code, ColdFusion returns the following JSON (Javascript Object Notation) data (I have manually added white-space for readability):
{
"JSONRPC": 2.0,
"error": {},
"result": [ "Joanna", "Sarah", "Tricia" ]
}
As expected, our control key - "jsonRPC" - which was defined using dot-notation, was serialized into an all-caps "JSONRPC." Our second and third keys - "error" and "result" - were defined using array-notation and maintained their original case in the JSON output. The exciting part of this, however, is the fact that once the "result" key was defined using array-notation, it was then further redefined several times using a friendlier dot-notation and still maintained the original key-casing.
In today's web climate, JSON (Javascript Object Notation) is a fact of life. It's a lean mean data representation format and is quickly becoming the premier choice for inter-application communication. When communication between a case-insensitive language like ColdFusion and a case-sensitive language like Javascript, the formatting of our data exchanges requires special care. Luckily, ColdFusion allows for both case-insensitivity and targeting key-casing using array-notation.
Want to use code from this post? Check out the license.
Reader Comments
@Ben,
I used to use the array notation in the beginning but have been using dot notation since last couple of years. It's much easier. One question: Do we have to define the key using array-notation? Couldn't we just define with dot-notation to begin with?
@Smita,
This array-notation / dot-notation conversation is very specifically for the creation of structs that will be serialized into JSON. I agree with you that when staying in ColdFusion, you should just use dot-notation from the very beginning; it is easier to both code and then to read.
But, if you *know* your Struct is going to be serialized into JSON, you'd be good to define the keys using array-notation such that the case of those keys is maintained during the serialization life-cycle. This way, you can be sure to deliver keys that match your target API client's expectations.
@Ben,
Ah! Got it! Thanks!
@Smita,
It is my pleasure :)
@Ben,
On second thought, it's a better practice to do this way always.. initialize using array-notation and then subsequent assignments can be done using dot-notations. Thanks for discovering this!
Awesome example, Ben. I am sure this will save dozens of people hours of frustration as they try to implement JSON-type calls from CF. Thanks for sharing your knowledge!
As a general rule I use array notation for defining everything and access the values using dot notation.
It is way safer that way because later on down the road you may want to expose some methods to an ajax proxy you can access the variables without worrying about having to go all caps.
@Paul,
Thanks my man - happy to share. I was pretty excited to realize that you could use dot-notation *after* array-notation. Will make my API code much easier to read.
@Smita, @Robert,
Yeah, that's a good point - you might not know what kind of road-map your application might have. I guess I was thinking more about things that I specifically want to make into APIs.
@Ben,
Allaire/Macromedia/Adobe documented that aspect of array notation back when they first introduced the XML functions and WDDX, because XML is also case-sensitive. But since then they haven't really emphasized it as much as they should.
"Camel case" (interCap format) is so much easier to read in cfdumps, I've been defining struct keys with array notation ever since I learned about it. But this article is really valuable, especially in the light of current documentation, which totally underemphasizes the importance of array notation.
Also, cfset Variables[Key] = value just looks better than cfset "Variables.#Key#" = value, doesn't it? Somehow putting a quoted string to the left of an assignment's equals sign just seems wrong.
I know this is a philosophical debate, but I find array notation way easier to use when working with dynamic variables. I think this is much cleaner:
rather than
(I'm not even sure that syntax works - I found it too weird to work with.)
In some apps that I've developed I have hundreds of dynamic vars that I have to loop over and array notation is much easier to work with. And, at that point, mixing array and dot notation looks odd and hinders moving code blocks.
Now that I'm thinking about it, I'm also not really a big fan of mixing dot/array notation when dealing with query outputs:
queryName.queryCol[1] vs queryName["queryCol"][1]
But, that's not really on topic...
@WebManWalking
We said the same thing, at the same time :) I completely agree.
@WebManWalking,
That makes sense - that they added the functionality when they added the XML features. That never even occurred to me. Honestly, I wasn't even sure if this was considered a "documented" feature or not.
@WebManWalking, @Aaron,
I'll certainly agree with you guys that for dynamic variables, the array notation is much easier to ready. It's rare that I use the quoted-variable approach to dynamic naming... rare, but not unheard of.
At the end of the day, I hesitate to go array-notation on everything as it prevents me from leveraging new syntax features like Implicit Struct Creation:
These will, of course, come back as all CAPS; but, unless I need them to be camelCase, the implicit struct creation is sooo nice :)
Would be nice to see if the save behavior exists if you define the return struct using implicit notation.
@Ben
Totally agree. I think it boils down to consistency. There's no magic bullet that works in all cases. You need to understand all the rules so you can break them. Reminds me of this post:
http://www.lifehack.org/articles/management/how-to-break-all-the-rules.html
#4 - The inexperienced rule-breaker breaks the rules because s/he doesn't know any better. The master rule-breaker breaks the rules because, after careful consideration, s/he has decided that the most effective and meaningful way to get something done was to break a rule. They have an explanation for every single step outside the accepted boundaries of the "right and proper".
@Daniel,
No, the implicit struct creation does NOT maintain case. At least not in CF8 (the only JRUN I have running at this moment :D ). I think there are plans to change this behavior in 9.01 or future versions. But not sure.
@Aaron,
Sounds good - reminds me of the zen-like passage from Eloquent Javascript:
www.bennadel.com/blog/2152-Eloquent-Javascript-A-Modern-Introduction-To-Programming-By-Marijn-Haverbeke.htm
Fu-Tzu had written a small program that was full of global state and dubious shortcuts. Reading it, a student asked 'You warned us against these techniques, yet I find them in your program. How can this be?' Fu-Tzu said, 'There is no need to fetch a water hose when the house is not on fire.' {This is not to be read as an encouragement of sloppy programming, but rather as a warning against neurotic adherence to rules of thumb.}
@Ben, I agree with you on the implicit struct notation. It does work though if you put the key name in quotes (like you can do in JavaScript) in CF 9.0.
The key in quotes keeps maintains the case, allowing you to also define dynamic keys.
@Mike,
Oh snappity snap - good call. The quoting of keys during implicit struct creation is *new* in ColdFusion 9 (for other people reading this). But, I had no idea that it maintained casing. Awesome find!!
@Ben:
This is awesome! I do have one question, though. When are you going to publish your book? Or have you already and I am just so out of the loop?
@Anna,
Ha ha, no, I have no book :) I just blog :)
You should, that'd be a good one to add to my collection.
Hey Ben,
maintaining key-case should also work if you use ColdFusions structInsert() function...
Thanks, Ben! That helped me!
This post is still helpful 2 years later. Thanks!
@AJ,
Thanks my man! Perhaps of further interest, I just released a ColdFusion component that helps maintain case and proper data-types for JSON serialization:
www.bennadel.com/blog/2505-JsonSerializer-cfc-A-Data-Serialization-Utility-For-ColdFusion.htm
This is the approach I've been using lately and it's been working quite well.