Sunday, February 20, 2011

ColdFusion: Flatten an XML object to a Struct

On a recent ColdFusion project I had to make a PDF file respond like a traditional web page. You know, add form fields with a submit button, capture the submitted data server-side, and show the new PDF with the data submitted by the user. Even though this Adobe technology has been around a while and I knew it was possible, I had never done it before. To accomplish this, I used several tools:

  • Adobe Professional - To add form fields and JavaScript validation
  • IText - To populate form fields and flatten the PDF

So began a new coding adventure and, again, I found myself intrigued by this software challenge. I created a simple PDF, added JavaScript validation via Adobe Professional, and then began playing around with how submission works. I discovered a few differences from a traditional HTML page, but that's another post.

I set the PDF form submit options to submit form field data as XML. I quickly discovered I had to "loop through" the PDF form fields in an XML object. However, I wanted the XML data converted into a ColdFusion structure.

Q: Surely someone has already written a function like this right?
A: Nope. Or at least I didn't find anyone doing it.

Fortunately, I did find something that helped me refactor a solution. On www.cflib.org I found a flattenStruct function. It recursively traversed through a structure and created a new structure, a flatten structure.

From this, I created a new function, flattenXmlToStruct.

<cffunction name="flattenXmlToStruct" access="public" output="false" returntype="struct">
<cfargument name="xmlObject" required="true" type="xml" />
<cfargument name="delimiter" required="false" type="string" default="." />
<cfargument name="prefix" required="false" type="string" default="" />
<cfargument name="stResult" required="false" type="struct" />
<cfargument name="addPrefix" required="false" type="boolean" default="true" />

<cfset var sKey = '' />
<cfif NOT isDefined("arguments.stResult")>
 <cfset arguments.stResult = structNew()>
</cfif>

<cfloop from="1" to="#ArrayLen(arguments.xmlObject.XmlChildren)#" index="arrIndx">

 <cfset sKey = arguments.xmlObject.XmlChildren[arrIndx].XmlName>

 <cfif ArrayLen(arguments.xmlObject.XmlChildren[arrIndx].XmlChildren) EQ 0>
   <cfif arguments.addPrefix and len(arguments.prefix)>
     <cfset arguments.stResult["#arguments.prefix##arguments.delimiter##sKey#"] =
                          arguments.xmlObject.XmlChildren[arrIndx].XmlText />
   <cfelse>
     <cfset arguments.stResult[sKey] =
                          arguments.xmlObject.XmlChildren[arrIndx].XmlText />
 </cfif>

 <cfelse> <!--- Node has more children... --->
   <cfif arguments.prefix EQ "">
     <cfset currentKey = sKey>
   <cfelse>
     <cfset currentKey = "#arguments.prefix##arguments.delimiter##sKey#">
   </cfif>

   <cfset flattenXmlToStruct(arguments.xmlObject.XmlChildren[arrIndx],
                                arguments.delimiter, currentKey, arguments.stResult) />

 </cfif>
</cfloop>

<cfreturn arguments.stResult />
</cffunction>

Saturday, July 10, 2010

Syntax Highlighter Example

Here's my first attempt at using the SyntaxHighlighter from Alex Gorbatchev.

It is one of the most popular sytnax highlighters on the web today. It has been around since 2004 and is still being supported. That's a mere feat in itself.

The example you see below is v2.1 of Alex's excellent tool. If you'd like to copy the source code, it's easy to snag. Move your mouse over the snippet of code and you'll see a toolbar appear. Mouse over the icons to see what your options are: View Source and many more.

<script>
alert("Hello world and good " + timeOfDay() + "!\nIt's gonna be good.  Really good." );

// Determine what time of day is it... in english! 
function timeOfDay(d) {
  if (d.getHours() >= 0 && d.getHours() < 12)
    return "morning";
  else if (d.getHours() >= 12 && d.getHours() < 18)
    return "afternoon";   
  else
    return "evening";     
} // end: timeOfDay
</script>