Twitter: raymondcamden


Address: Lafayette, LA, USA

Implementing custom JSON serialization for your CFCs

09-04-2013 4,070 views ColdFusion 16 Comments

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");

16 Comments

  • Commented on 09-04-2013 at 7:57 AM
    Neato Bandito!!! Nice going Ben, I sure will be giving this a try this week!!
  • Commented on 09-04-2013 at 8:38 AM
    Ahem, Ray wrote this blog entry. ;) (Ok, Ben gets the credit, but you don't want to hurt my fragile ego, do you???)
  • Commented on 09-04-2013 at 8:43 AM
    Ok Ok Mr Ray :) Great job both of you guys!!!!
    You know you're the man Ray :)
  • Commented on 09-04-2013 at 8:44 AM
    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.
  • Commented on 09-04-2013 at 9:23 AM
    "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?
  • Commented on 09-04-2013 at 9:26 AM
    "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.
  • Commented on 09-04-2013 at 9:33 AM
    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.
  • Commented on 09-04-2013 at 9:34 AM
    @Ben: Oh slick - that is well done. (I'll be honest - I skim/read. ;)
  • Commented on 09-04-2013 at 9:39 AM
    No worries :)
  • Commented on 09-04-2013 at 9:54 AM
    You may also want/need to explicitly content type to application/json:

    <cfcontent type="application/json" />
  • Commented on 09-04-2013 at 10:00 AM
    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.
  • Commented on 09-04-2013 at 10:01 AM
    ER is here - folks should vote for it. I'm going to do so now:

    https://bugbase.adobe.com/index.cfm?event=bug&...
  • Commented on 09-04-2013 at 5:59 PM
    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
  • Commented on 09-04-2013 at 6:23 PM
    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.
  • Commented on 10-24-2013 at 2: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
  • Commented on 10-24-2013 at 7:41 AM
    It should be. I haven't tried it that way.

Post Reply

Please refrain from posting large blocks of code as a comment. Use Pastebin or Gists instead. Text wrapped in asterisks (*) will be bold and text wrapped in underscores (_) will be italicized.

Leave this field empty