ramar.work

CF & JSON: A Match Made in Heaven?

Lucee (and other CFML engines) were based off of the idea that case-insensitivity is a good thing. At one point, this was probably true. For anyone who has worked with it recently though, one of CFML’s biggest limitations seems to be dealing with JSON. It’s Serialization functions return a string with uppercase keys regardless of what kind of data you feed it. For me, it caused quite a few annoying bugs when dealing with API responses in Javascript. Fortunately, a fix is pretty easy to come by–and if you are tasked with designing an API or SOAP resource with ColdFusion, you may find this interesting.

To handle JSON serialization, CFML takes its binary representation of your object (usually a struct) and turns into a “stringified map” if you will. So assuming we feed it an item (storeItem here) following some sort of hierarchical format:

storeItem = {
        title = "Weezy Ball"
    ,   description = "Fun for Dogs, Cats and bozos alike"
    ,   cost = 3.96
    ,   keywords = [ "shine", "on", "you", "crazy", "diamond" ]
    ,   colors  = {
            white = "0xfffff"
        ,   black = "0x000000"
        ,   green = "0x00ff00"
    }
}

…we’ll come out with something like the following after running SerializeJSON.

//INPUT:
siJson = SerializeJSON( storeItem );
writeoutput( siJson );

//OUTPUT: (Eww, uppercase JSON keys...)
{
    "KEYWORDS":["shine","on","you","crazy","diamond"]
,   "COLORS":{
        "BLACK":"0x000000"
    ,   "GREEN":"0x00ff00"
    ,   "WHITE":"0xfffff"
    },
    "COST":3.96
,   "TITLE":"Weezy Ball"
,   "DESCRIPTION":"Fun for Dogs, Cats and bozos alike"
}

So, now we have a string representation of what was once a binary object. This would work for all intents and purposes, but it’s not natural to a user (I guess the developer in this case) and does not follow convention of any other common APIs. We could run some kind of mapping function in Javascript to fix it too, but that would be equally cumbersome as we’d not only have to deal with our server’s responses, but we’d also have to deal with response correctness on the client side. Aggravating.

To make something a little more tasteful (and more useful out of the box with plain old Javascript or Dart) we really need to change those uppercase key values to lowercase. That boils down to a fairly straightforward regular expression. CFML’s regex functions return a string if the regex was found. Otherwise, they return an unchanged copy. So re-using our store item from above.


//INPUT:
siJson = SerializeJson( storeItem );
siJson = REReplace( siJson, '"([[:upper:]]*)":', '"\\L\\1":', "ALL" );

//OUTPUT: (Ah, now that's something I can deal with)
{
    "keywords":["shine","on","you","crazy","diamond"]
,   "colors": {
        "black":"0x000000"
    ,   "green":"0x00ff00"
    ,   "white":"0xfffff"
    }
,   "cost":3.96
,   "title":"Weezy Ball"
,   "description":"Fun for Dogs, Cats and bozos alike"
}

Hope this helps others who may run into such a situation. I was plenty annoyed on a number of occasions by this, and could not find a simple solution for it online.