Ben Nadel
On User Experience (UX) Design, JavaScript, ColdFusion, Node.js, Life, and Love.
I am the chief technical officer at InVision App, Inc - a prototyping and collaboration platform for designers, built by designers. I also rock out in JavaScript and ColdFusion 24x7.
Meanwhile on Twitter
Loading latest tweet...
Ben Nadel at Scotch On The Rock (SOTR) 2010 (Munich) with: Christian Etbauer

Maintaining Key-Case During JSON Serialization In ColdFusion

By Ben Nadel on
Tags: 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.




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?

Reply to this Comment

@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.

Reply to this Comment

@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!

Reply to this Comment

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!

Reply to this Comment

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.

Reply to this Comment

@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.

Reply to this Comment

@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.

Reply to this Comment

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:

  • <cfloop from=1 to=100 index=i>
  • <cfset session["repeater-" & i]=form["repeater-" & i]>
  • </cfloop>

rather than

  • <cfset "session.repeater-#i#"="attributes.repeater-#i#">

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

Reply to this Comment

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

  • <cfset data = {
  • foo = "bar",
  • boo = "ya!"
  • }

These will, of course, come back as all CAPS; but, unless I need them to be camelCase, the implicit struct creation is sooo nice :)

Reply to this Comment

@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".

Reply to this Comment

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

http://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.}

Reply to this Comment

@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.

  • i = 0;
  •  
  • foo = {
  • normalTest = "bar",
  • "caseTest" = "jazz",
  • "caseTest_#i#" = "jazz bar"
  • };
  •  
  • writeDump( foo );

The key in quotes keeps maintains the case, allowing you to also define dynamic keys.

Reply to this Comment

@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!!

Reply to this Comment

@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?

Reply to this Comment

Hey Ben,

maintaining key-case should also work if you use ColdFusions structInsert() function...

Reply to this Comment

Post A Comment

You — Get Out Of My Dreams, Get Into My Comments
Live in the Now
Oops!
Comment Etiquette: Please do not post spam. Please keep the comments on-topic. Please do not post unrelated questions or large chunks of code. And, above all, please be nice to each other - we're trying to have a good conversation here.