Quick and Dirty JSON/Query Example

Yesterday in the ColdFusion chat room someone asked a question (a technical question at that - guess how rare that is?) about how they could use a JSON-encoded query. I whipped up a quick example that I thought others might like as well. This example does not use Ajax to load the JSON data - which is also pretty rare - but I wanted something that I could run all in one file. So here we go...

The first thing I did was get a query of data and serialize it using SerializeJSON:

<cfquery name="getstuff" datasource="blogdev" maxrows="5">
select   id, title, posted
from   tblblogentries
</cfquery>

<cfset jsondata = serializeJSON(getstuff)>

At this point, jsondata is a string. Here is what it looked like:

{"COLUMNS":["ID","TITLE","POSTED"],"DATA":[["905D9689-0130-1A16-62272F586A771C0C","mmm","May, 15 2007 10:31:00"],["905DAAED-E300-3B9D-7E25E43985CA9507","nn","May, 15 2007 10:31:00"],["905DE931-B8DB-B1AD-F77FC505851C2E9A","j","May, 15 2007 10:31:00"],["905E0C6A-99B8-97EC-4862D9064B9EC659","mmmmm","May, 15 2007 10:31:00"],["905E5910-0E2D-C566-5DCD39E6FD48ED06","NUMBER 11","May, 15 2007 10:32:00"]]}

I wanted to work with this on the client side (the whole point of this entry), so I needed to set this data to a JavaScript variable. The cool thing about JSON is that it can be evaled (think of this like ColdFusion's evaluate function) directly to a variable, so I used this code to assign it:

<script>
mydata = eval(<cfoutput>#jsondata#</cfoutput>)

For my demo, I built a quick form with a button. The idea is that you would hit the button, and I'd then run code that would loop over the query and display the contents. Here is the form I used and the 'area' I would use for output:

<input type="button" value="Show Data" onClick="showData()">
<div id="content" />

Ok, nothing complex yet. Now let's take a look at showData():

function showData() {
   var output = document.getElementById('content');
   var colMap = new Object();
   
   //first - find my columns
   for(var i = 0; i < mydata.COLUMNS.length; i++) {
      colMap[mydata.COLUMNS[i]] = i;      
   }
   
   for(var i = 0; i < mydata.DATA.length; i++) {
      var str = mydata.DATA[i][colMap["TITLE"]] + " was posted at " + mydata.DATA[i][colMap["POSTED"]] + "<br />";
      output.innerHTML += str;
   }
}

The first line of code simply creates a pointer to my div. I'll be writing out to that later. Now I need to loop over my query. The question is - how? If you look at the JSON string I output earlier, you will see that it is an object with two main properties - columns and data. The columns property is a list of columns. The data property contains my rows of data. Note though that it isn't indexed by column names. Instead - the first item in the first row of data matches the first column name. So what I need to do is figure out what my columns are. To do this I create a column map - i.e., a mapping of columns to indexes. The first FOR loop does this.

Once I have that - then it is a trivial matter to loop over my rows of data and pick the values I need. So for example, to get the title for row i, I use:

mydata.DATA[i][colMap["TITLE"]]

Make sense? I'll include the full source of the code below, but before I do, a few notes:

  1. The SerializeJSON function takes an optional second argument that is only used for queries. If set to true, the structure is pretty different than what I described above. I'll blog about that later today. (Someone will probably need to remind me.)
  2. While SerializeJSON is new to ColdFusion 8, ColdFusion 7 introduced the toScript function, which is another way to go from a ColdFusion variable to a JavaScript variable. (It is actually simpler than what I used above.)

Now for the complete template:

<cfquery name="getstuff" datasource="blogdev" maxrows="5">
select   id, title, posted
from   tblblogentries
</cfquery>

<cfset jsondata = serializeJSON(getstuff)>

<script>
mydata = eval(<cfoutput>#jsondata#</cfoutput>)

function showData() {
   var output = document.getElementById('content');
   var colMap = new Object();
   
   //first - find my columnsco
   for(var i = 0; i < mydata.COLUMNS.length; i++) {
      colMap[mydata.COLUMNS[i]] = i;      
   }
   
   for(var i = 0; i < mydata.DATA.length; i++) {
      var str = mydata.DATA[i][colMap["TITLE"]] + " was posted at " + mydata.DATA[i][colMap["POSTED"]] + "<br />";
      output.innerHTML += str;
   }
}
</script>

<input type="button" value="Show Data" onClick="showData()">
<div id="content" />

Comments

I am so glad to see Coldfusion embrace JSON. It is my preferred format of choice for data in JS. Can't wait to start using some of these features more regularly.
# Posted By Mike Benner | 9/24/07 10:35 PM
I would also like to add that this might not be that benefical to CF8, but for those wanting to have some JSON in there previous CF instances.

CFJsonService on RiaForge is worth a look at too.
# Posted By Andrew Scott | 9/26/07 1:17 AM
I like the data rendering speed aspect of the SerializeJSON function, however, I'm concerned about business logic implementation within the data output stream with this method, the js code would be a mess to read vs. cf code for the data output part.
# Posted By Don Li | 3/19/08 3:07 PM
Don Li - this example was a mash up so I could get everything on one page. Normally you would have a page request the JSON via an Ajax request.
# Posted By Raymond Camden | 3/19/08 3:11 PM
How do you reformat the date to something any normal person would want to see, e.g. mm/dd/yyy?
# Posted By Michael Damian | 6/9/08 1:58 AM
You can do it in JS, but it can be a pain. I'd just do it server side with dateFormat.
# Posted By Raymond Camden | 6/9/08 6:16 AM
Thanks. Could we get a quick snippet of that in action please?
# Posted By Michael Damian | 6/9/08 6:59 AM
dateformat? Please see the CF reference. It's pretty easy.
# Posted By Raymond Camden | 6/9/08 7:02 AM
Yes, we all know how easy dateformat is to use. But how do you weld it into this JSON pallava. Just a quick example would do wonders and gain my utmost gratitude. Come on, it won't kill you:-)
# Posted By Michael Damian | 6/9/08 7:09 AM
But that's the thing - it doesn't need a quick example. If your query is being serialized into json, you would just use dateFormat on the query -before- your serialize it. Really - dateFormat is _extremely_ simple.
# Posted By Raymond Camden | 6/9/08 7:12 AM
Yes, Raymond, dateformat is easy for simple date outputs in a different format sans JSON. Anyway, I've figured out how to weld it into your JSON example. Will post some code shortly for others needing an example.
# Posted By Michael Damian | 6/12/08 6:04 AM
Be sure to post a url here. I think I may be missing something as I don't see the need - so once you blog it will probably make more sense to me. :)
# Posted By Raymond Camden | 6/12/08 6:17 AM