Implementing custom JSON serialization for your CFCs

This post is more than 2 years old.

A few weeks back Ben Nadel released a nice utility that does JSON serialization with ColdFusion data. As you know, ColdFusion has this feature baked in, but it doesn't always serialize your data in the form you need. Ben's library gives you greater control over JSON serialization and can be helpful in cases where the built-in support isn't working right for you. I wanted to share a quick example of how you could possibly use this in a real CFC.

First, let's consider a simple CFC built to return an array of data in JSON:

Nothing too fancy here, right? I've got an array of structs where each element in the array is a structure with three keys. There's a name, an age, and a favorite color key. You would typically retrieve this data by calling the CFC and passing the method and returnformat URL keys:

http://localhost/test.cfc?method=demo&returnformat=json

And here is the result:

[{"FAVCOLOR":333333,"NAME":"Ray 1","AGE":29}, {"FAVCOLOR":333333,"NAME":"Ray 2","AGE":48}, {"FAVCOLOR":333333,"NAME":"Ray 3","AGE":41}, {"FAVCOLOR":333333,"NAME":"Ray 4","AGE":68}, {"FAVCOLOR":333333,"NAME":"Ray 5","AGE":21}, {"FAVCOLOR":333333,"NAME":"Ray 6","AGE":31}, {"FAVCOLOR":333333,"NAME":"Ray 7","AGE":32}, {"FAVCOLOR":333333,"NAME":"Ray 8","AGE":78}, {"FAVCOLOR":333333,"NAME":"Ray 9","AGE":94}]

There are two problems with this result. First, the keys are uppercased. That isn't too difficult to fix nor is it too difficult to handle on the client-side. Secondly, the "favcolor" value was transformed into a number. This is definitely not desirable. Unfortunately you can't fix this easily unless you mess with your data a bit.

So how would you fix this with Ben's utility? Consider this updated version:

Let's go over the changes here one by one.

  • First, note we have to change the return type of the method from array to string. While we are still working with an array, the actual return now will be a JSON string, not the original array.
  • The next change isn't necessarily required, but it helps. We override the return format value and set it to plain. The value "plain" tells ColdFusion to not do anything with the result. I.e., leave it be and don't touch it. In theory the end user could just pass this in the URL, but we can make things even easier by just setting the format in the CFC.
  • After the existing business logic, we then simply instantiate Ben's CFC and serialize the data. We can then return that string.

Now we can call our logic like so:

http://localhost/test.cfc?method=demo2

And get this in response:

[{"favcolor":"333333","name":"Ray 1","age":"37"}, {"favcolor":"333333","name":"Ray 2","age":"88"}, {"favcolor":"333333","name":"Ray 3","age":"86"},{"favcolor":"333333","name":"Ray 4","age":"25"}, {"favcolor":"333333","name":"Ray 5","age":"72"}, {"favcolor":"333333","name":"Ray 6","age":"97"}, {"favcolor":"333333","name":"Ray 7","age":"32"}, {"favcolor":"333333","name":"Ray 8","age":"56"}, {"favcolor":"333333","name":"Ray 9","age":"57"}]

Note that by default the keys were lowercased, which is pretty darn cool. Also note that age was transformed into a string. If you don't like this, you can fix it like so:

var serializer = new JsonSerializer().asString("favcolor").asInteger("age");

Raymond Camden's Picture

About Raymond Camden

Raymond is a senior developer evangelist for Adobe. He focuses on document services, JavaScript, and enterprise cat demos. If you like this article, please consider visiting my Amazon Wishlist or donating via PayPal to show your support. You can even buy me a coffee!

Lafayette, LA https://www.raymondcamden.com

Archived Comments

Comment 1 by Jaana Gilbert posted on 9/4/2013 at 4:57 PM

Neato Bandito!!! Nice going Ben, I sure will be giving this a try this week!!

Comment 2 by Raymond Camden posted on 9/4/2013 at 5:38 PM

Ahem, Ray wrote this blog entry. ;) (Ok, Ben gets the credit, but you don't want to hurt my fragile ego, do you???)

Comment 3 by Jaana Gilbert posted on 9/4/2013 at 5:43 PM

Ok Ok Mr Ray :) Great job both of you guys!!!!
You know you're the man Ray :)

Comment 4 by Raymond Camden posted on 9/4/2013 at 5:44 PM

Heh, thank you, ego saved. :) Personally - I still think folks get confused by returnformat and CFC calls. So this was a great opportunity to show an example of it.

Comment 5 by Phillip Senn posted on 9/4/2013 at 6:23 PM

"the keys were lowercased".

Q: What if it were FavColor instead of favcolor? Are you saying that using url.returnformat = "plain" leaves the case alone?

Comment 6 by Raymond Camden posted on 9/4/2013 at 6:26 PM

"Q: What if it were FavColor instead of favcolor?"

Doesn't change. I believe Ben's code forces lowercase. Just checked his code - and it does. To me, this is good.

"Are you saying that using url.returnformat = "plain" leaves the case alone?"

No. When you use returnformat=plain, ColdFusion does nothing to the result. When you use returnformat=wddx or json, CF does serialization. If you use returnformat=plain, you MUST return a simple result (string, date, number, you get the idea).

So basically my point is - if you don't like the built-in serialization and want to use your own (or Ben's), use the plain format and handle it yourself.

Comment 7 by Ben Nadel posted on 9/4/2013 at 6:33 PM

Woot!! Thank you Ray :D Now, this is what I call a good Hump day!

@Phillip, Re: casing of keys, it will use what ever you pass into the asXYZ() methods. So, if you pass .asString( "FavColor" ), the keys will come through as "FavColor". This holds true for all the .as() methods.

By default, it will lowercase all the keys that it doesn't recognize. So, if it reaches part of data structure that was not explicitly defined, it will just lowercase the keys.

Comment 8 by Raymond Camden posted on 9/4/2013 at 6:34 PM

@Ben: Oh slick - that is well done. (I'll be honest - I skim/read. ;)

Comment 9 by Ben Nadel posted on 9/4/2013 at 6:39 PM

No worries :)

Comment 10 by Dan G. Switzer, II posted on 9/4/2013 at 6:54 PM

You may also want/need to explicitly content type to application/json:

<cfcontent type="application/json" />

Comment 11 by Raymond Camden posted on 9/4/2013 at 7:00 PM

Good point there. Interestingly enough you can't do it in cfscript. I'm assuming you would need to do it with getPageContext, which is workable, but not acceptable. Checking the bug tracker.

Comment 12 by Raymond Camden posted on 9/4/2013 at 7:01 PM

ER is here - folks should vote for it. I'm going to do so now:

https://bugbase.adobe.com/i...

Comment 13 by Adam Cameron posted on 9/5/2013 at 2:59 AM

I dunno if I'm being overly dogmatic about encapsulation, but is there no better way of dealing with URL.returnformat than accessing it directly within one's method? For a remote call with returnFormat on the URL, it should be passed into the arguments scope, shouldn't it?

(the above is very much a question, not a statement dressed up as one)

--
Adam

Comment 14 by Raymond Camden posted on 9/5/2013 at 3:23 AM

Good question Adam. Obviously it works (hard coding it), but is it "The Right Thing" (and I know that is not something folks agree on all the time).

In my opinion - returnformat is a handy way to allow a service to dynamically return JSON or WDDX or plain text for a remote client.

But in *practical* usage, I'm willing to bet that 99% of your use of this CFC will be for AJAX, and therefore, json.

So by adding a url.returnformat to the method, I make it easier for the consumer which to me is a Good Thing as well. Now I can just call soandso.cfc?method=foo and not worry about asking for JSON when JSON is the only thing that would ever make sense.

My 2 cents.

Comment 15 by Adam Cameron posted on 10/24/2013 at 11:28 AM

Sorry Ray, you missed my point. What I meant was that rather than using this:

url.returnformat = "plain";

Can you not use:

arguments.returnformat = "plain";

For remote calls, aren't all URL params passed into the method as arguments?

--
Adam

Comment 16 by Raymond Camden posted on 10/24/2013 at 4:41 PM

It should be. I haven't tried it that way.