Skip to main content
Ben Nadel at cf.Objective() 2014 (Bloomington, MN) with: Kai Koenig
Ben Nadel at cf.Objective() 2014 (Bloomington, MN) with: Kai Koenig

(CF)JSON - My Own ColdFusion Version For AJAX

By
Published in , Comments (12)

After going through CFJSON, I created my own version with my own coding standards and quirkiness. To point out some non-superficial changes, my version:

Does not have any try-catch blocks. I don't get the point. I don't see where the code can really fail. I am only checking standard types that should have standard values. I can't see what would break.

For the query conversion, I am not including the column list or the recordcount. I think that's pointless. I am not interested in the meta-data. I only want the records. I honestly cannot think of any situation where I would not know what columns I was dealing with before I even made the AJAX call to the ColdFusion server.

Another query conversion difference is that fact that my query is returned as an array of "row" structures rather than CFJSON's array of "column" structures. I am not crazy about ColdFusion's implementation direct access query notation and I don't want to carry that over to the Javascript side of things. The down side of this is that my conversion returns more data as the column names are repeated keys for each record of the query. I don't think this is such a huge issue though as I do not intend to deal with massive amounts of data.

So, without further delay, my version of CFJSON:

<cffunction name="ToJavascript" access="public" returntype="string" output="false"
hint="Based on CFJASON {http://jehiah.com/projects/cfjson/}, this converts ColdFusion structures to Javascript Object Notation.">

	<cfargument name="Data" type="any" required="yes" />

	<cfscript>

		// Define the local scope.
		var LOCAL = StructNew();

		// Create an object to store the output. We are going to use
		// a java string buffer as there may be a large amount of
		// concatination.
		LOCAL.Output = CreateObject( "java", "java.lang.StringBuffer" ).Init();

		// Check to see if the data is an array.
		if (IsArray( ARGUMENTS.Data )){

			// Loop over the array to encode the items.
			for (LOCAL.Index = 1 ; LOCAL.Index LTE ArrayLen( ARGUMENTS.Data ) ; LOCAL.Index = (LOCAL.Index + 1)){

				// Encode the value at this index. Call the function
				// recursively as this could be any kind of data.
				LOCAL.Value = ToJavascript( ARGUMENTS.Data[ LOCAL.Index ] );

				// Check to see if we are appending to a current value.
				if (LOCAL.Output.Length()){
					LOCAL.Output.Append( "," );
				}

				// Append the encoded value.
				LOCAL.Output.Append( LOCAL.Value );

			}

			// Return the encoded values in an array notation.
			return( "[" & LOCAL.Output.ToString() & "]" );

		// Check to see if we have a structure.
		} else if (IsStruct( ARGUMENTS.Data )){

			// Check to see if the structure is empty. If it is, then
			// we don't have to do any more work, just return the
			// empty object notation.
			if (StructIsEmpty( ARGUMENTS.Data )){
				return( "{}" );
			}

			// Get the array of keys in the structure.
			LOCAL.Keys = StructKeyArray( ARGUMENTS.Data );

			// Loop over the keys in the structure.
			for (LOCAL.Index = 1 ; LOCAL.Index LTE ArrayLen( LOCAL.Keys ) ; LOCAL.Index = (LOCAL.Index + 1)){

				// Encode the value at this index. Call the function
				// recursively as this could be any kind of data.
				LOCAL.Value = ToJavascript( ARGUMENTS.Data[ LOCAL.Keys[LOCAL.Index] ] );

				// Check to see if we are appending to a current value.
				if (LOCAL.Output.Length()){
					LOCAL.Output.Append( "," );
				}

				// Append the encoded value.
				LOCAL.Output.Append( """" & LCase( LOCAL.Keys[LOCAL.Index] ) & """:" & LOCAL.Value );

			}

			// Return the encoded values in an object notation.
			return( "{" & LOCAL.Output.ToString() & "}" );

		// Check to see if this is some sort of other object.
		} else if (IsObject( ARGUMENTS.Data )){

			// We found an object that is not a built in type...
			// return an unknown type.
			return( "unknown-obj" );

		// Check to see if we have a simple, numeric value.
		} else if (IsSimpleValue( ARGUMENTS.Data ) AND IsNumeric( ARGUMENTS.Data )){

			// Return the number as a string.
			return( ToString( ARGUMENTS.Data ) );

		// Check to see if we have a simple value.
		} else if (IsSimpleValue( ARGUMENTS.Data )){

			// Return the value encoded for Javascript.
			return( """" & JSStringFormat( ToString( ARGUMENTS.Data ) ) & """" );

		// Check to see if we have a query.
		} else if (IsQuery( ARGUMENTS.Data )){

			// We are going to convert the query into an array or
			// structures. This is going to be somewhat slower than
			// going straight from the query to javascript, but I
			// think it will make the query more usable.

			// Start by getting an array of the columns.
			LOCAL.Columns = ListToArray( ARGUMENTS.Data.ColumnList );

			// Create an array for the value.
			LOCAL.TempData = ArrayNew( 1 );

			// Loop over the rows in the query to create structures.
			for (LOCAL.RowIndex = 1 ; LOCAL.RowIndex LTE ARGUMENTS.Data.RecordCount ; LOCAL.RowIndex = (LOCAL.RowIndex + 1)){

				// Create a structure for the current row.
				LOCAL.TempRow = StructNew();

				// Loop over the columns to add values to the strucutre.
				for (LOCAL.Column = 1 ; LOCAL.Column LTE ArrayLen( LOCAL.Columns ) ; LOCAL.Column = (LOCAL.Column + 1)){

					// Add the column value to the structure.
					LOCAL.TempRow[ LOCAL.Columns[ LOCAL.Column ] ] = ARGUMENTS.Data[ LOCAL.Columns[ LOCAL.Column ] ][ LOCAL.RowIndex ];

				}

				// Append the structure to the data array.
				ArrayAppend( LOCAL.TempData, LOCAL.TempRow );

			}

			// ASSERT: At this point, we have converted the query
			// into array of structs. Now encode it.

			// No need to return with object notation since the JS
			// encoding of the array will take care of that for us.
			return( ToJavascript( LOCAL.TempData ) );

		// Check for default case.
		} else {

			// If we got this far, then we found a type that we
			// are not able to serialize.
			return( "unknown" );

		}

	</cfscript>
</cffunction>

Want to use code from this post? Check out the license.

Reader Comments

1 Comments

nice work! I'm already using your code in production. Serializing rows instead of columns is exactly the way to go.

1 Comments

Year ago I was trying to create own framework for working with AJAX too, but now I understood that it is easily to use Prototype and more easier create other projects with it.

1 Comments

nice work! I'm already using your code in production. Serializing rows instead of columns is exactly the way to go.

1 Comments

Thanks Ben. Am using this to feed Yahoo's YUI library from CF7. I tried CFJSON first but couldn't work out how to tell YUI that the data was in columns. That became a no-brainer when delivered as rows.

Great work. Might be an old post, but still giving mileage.

15,848 Comments

@Todd,

My pleasure good man. If you are using ColdFusion 8+, however, there are more easy ways of doing this, using the SerializeJSON() method.

12 Comments

@all, toJavascript() will actually will escape single quotes which will generate invalid JSON (single quotes should not be escaped in JSON)

To fix this I just unescape the single quotes in Bens code with something like this:

replace(ToJavascript( whatevervar),"\'","'","ALL")

Hope this helps

I believe in love. I believe in compassion. I believe in human rights. I believe that we can afford to give more of these gifts to the world around us because it costs us nothing to be decent and kind and understanding. And, I want you to know that when you land on this site, you are accepted for who you are, no matter how you identify, what truths you live, or whatever kind of goofy shit makes you feel alive! Rock on with your bad self!
Ben Nadel