erichynds

Hi, I'm Eric Hynds, a front-end website developer living outside of Boston, Massachusetts. I'm passionate about developing functional, standard-compliant, and user-friendly websites.

A Quick Gotcha With CFML & JSON Serialization

ColdFusion internally represents structure keys in uppercase when the keys are created using dot notation. Dot notation is typically how programmers write their code and is considered to be best practice when working with structures. To the same effect, ColdFusion’s implementation of serializeJSON() serializes your object with respect to case; therefore, we might end up with something like this:

<cfset struct = {} />
<cfset struct.foo = "bar" />
 
<cfdump var="#serializeJSON(struct)#" />
 
<!---
	Result:  {"FOO":"bar"}
--->

Bummer. You’d probably expect “FOO” to be lowercase since that’s how you wrote it, but unfortunately it’s uppercase according to CF. Serializing a query forces uppercase JSON keys as well because queries act and function similarly to structures. This is rather annoying seeing how JavaScript is case sensitive, and serializeJSON is the way to push data from CF to JS. jQuery UI’s Autocomplete is a fine example of a widget that was not designed with case in mind; your JSON is expected to be returned with either a label or value key or both… in lowercase.

If you’re trying to serialize a query, there isn’t much you can do other than converting it to a structure and avoiding dot notation. Use bracket syntax, structInsert(), or create your structure literally:

<!--- 
	three different ways to add values to a structure 
	while forcing lowercase keys 
--->
<cfset struct1 = {} />
<cfset struct1["foo"] = "bar" />
 
<cfset struct2 = { "foo" = "bar" } />
 
<cfset struct3 = {} />
<cfset structInsert(struct3, "foo", "bar") />
 
<cfdump var="#serializeJSON(struct1)#" /> <!--- Result:  {"foo":"bar"} --->
<cfdump var="#serializeJSON(struct2)#" /> <!--- Result:  {"foo":"bar"} --->
<cfdump var="#serializeJSON(struct3)#" /> <!--- Result:  {"foo":"bar"} --->

If you’re using a remote URL for your source parameter with the jQuery UI Autocomplete widget, most likely you’re using a query to retrieve matches from a database. Converting a CF query to JSON has it’s own set of problems because you’re stuck with extraneous data you probably don’t want: “COLUMNS”, “DATA”, and “ROWCOUNT” depending on how you serialize it. Either way, you’ll never be able to pass a serializeJSON(query) directly to the widget, so let’s kill two birds with one stone by converting it to a structure and forcing the keys to a lowercase string:

<!--- get the matches --->
<cfquery name="q" datasource="#request.ds#">
select product_id as value, product_title as label
from products
where product_title like <cfqueryparam value="%#url.term#%" />
</cfquery>
 
<!--- where we'll hold the data to convert to JSON --->
<cfset data = [] />
 
<cfoutput query="q">
 
	<!--- create a new structure on each iteration, forcing lowercase keys --->
	<cfset obj = {
		"label" = label,
		"value" = value
	} />
 
	<!--- push this match onto the array --->
	<cfset arrayappend(data, obj) />
</cfoutput>
 
<!--- serialize the new structure --->
<cfoutput>#serializeJSON(data)#</cfoutput>

Easy peasy, nice and hackish.

Tags: , , ,

  • Anonymous

    test

  • JohnG832

    This was helpful. Thank you.

  • Brian Lang

    Found you via Google and your solution for pushing a query to an object and appending to an array was exactly what I needed. I did modify it a bit – I used cfloop instead of cfoutput.

    Thanks!