e4x for ColdFusion, sort of…
Coming from a Flex background I can’t imagine working with XML and not having e4x available. As you most likely know ColdFusion doesn’t have support for this fantastic notation. It has it’s own, clunky syntax for accessing XML elements within the document and an XmlSearch() function to query the document with XPath. I used to like XPath – until I saw e4x.
While I was waking up today I had this crazy idea – add e4x to CF. Here is what I came up with, a <cfx_e4x /> custom tag. Mind you, it’s only a couple of hours of work but it seems to be usable. I have tested this with CF9 on Java 6.
Basic syntax:
<cfscript>
x = xmlParse("<rootTag><tag attr='1'>value 1</tag><tag attr='2' nextAttr='meh'>value 2</tag></rootTag>");
</cfscript>
<cfx_e4x xml="#x#" e4x="root.tag.(@attr == 2).@nextAttr" />
<cfoutput>#xmlParse(cfx_e4x).result.xmlText#</cfoutput>
The above code will print meh. Cool, but what exactly happens here?
Lets start with where root. in an e4x expression comes from. Behind the scenes this tag uses Rhino to execute an e4x expression on the XML data. To get that working the XML has to be assigned to the variable – this variable is called root. But you can control what this variable is called. If you wanted to call it lol your call would look:
... <cfx_e4x xml="#x#" e4x="lol.tag.(@attr == 2).@nextAttr" e4xRoot="lol" /> <cfoutput>#xmlParse(cfx_e4x).result.xmlText#</cfoutput>
I’m sure you have noticed that the result of the tag was again parsed with xmlParse(). There is a very important reason for that. Depending of what the result of e4x expression is you may get a simple value, single or multiple XML elements in return. To make all these types of responses easily available cfx_e4x is wrapping the result into a top XML element. By default this element is called result. If your e4x was:
root.tag
the response in plain text would look like this:
<tag attr="1">value 1</tag> <tag attr="2" nextAttr="meh">value 2</tag>
You wouldn’t be able to parse it easily without adding the root node yourself. cfx_e4x will return:
<result> <tag attr="1">value 1</tag> <tag attr="2" nextAttr="meh">value 2</tag> </result>
Again, you can control the name of root node name using resultName attribute. If you execute the tag in a following way:
<cfx_e4x xml="#x#" e4x="lol.tag.(@attr == 2).@nextAttr" e4xRoot="lol" resultName="top" />
you would output the result using:
<cfoutput>#xmlParse(cfx_e4x).top.xmlText#</cfoutput>
Last to mention are namespaces. I found this XML on stackoverflow:
<rdf:RDF
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns="http://purl.org/rss/1.0/">
<rdf:item value="5"/>
<item value="10"/>
</rdf:RDF>
What if you wanted to get the values attributes for rdf:item and item elements? You would have to manually tell cfx_e4x what are the namespaces. Take a look at the code:
<cfscript> x = xmlParse( "<rdf:RDF xmlns:rdf=""http://www.w3.org/1999/02/22-rdf-syntax-ns##"" xmlns=""http://purl.org/rss/1.0/""> <rdf:item value=""5""/> <item value=""10""/> </rdf:RDF>" ); </cfscript> <cfx_e4x xml="#x#" e4x="root.reg::item.@value" ns_rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns##" ns_reg="http://purl.org/rss/1.0/" /> <cfdump var="#xmlParse(cfx_e4x)#" />
By using ns_... attribute you can tell what namespaces are being used. The only one you have to watch out for is the default namesapce. You have to name it – here it is named reg.
cfx_e4x is just a proof of concept. The code is available on github under an MIT license.
To install and use, download latest Rhino version, unzip/untar and copy js.jar into {CFROOT}/lib directory. Download CFXe4x.jar from github or build from source and drop it in the same directory. Restart ColdFusion and register a custom tag with name cfx_e4x and class uk.co.appembassy.cf.e4x.CFXe4x.