ColdFusion Zeus POTW - XPath/XLST Updates

For the most part, I try to avoid XML. I almost always go towards using JSON - both as a consumer and a producer. That being said, probably the only thing I find cool about XML is XPath and XSLT. I haven’t used XSLT much, but XPath can be a real useful tool. It allows you to perform queries against XML data (something you can’t do with JSON as far as I know). In ColdFusion Zeus, we’ve upgraded both XSLT and Xpath to version 2 of the specification. Here’s some details on this update.

First off - if you want to know everything about XPath2, you can hit up a few URLs to do some deep research:

  • Official Spec (exciting! read it to your kids! you won't put it down!)
  • Wikipedia entry (much simpler, skip the one above and read this one)
  • What's new in XPath2 (from XML.com) </ul>

    Now that you've read up, let's look at a few examples. First, our sample XML.

    <?xml version="1.0" encoding="ISO-8859-1"?> <bookstore> <book> <title lang="eng">Harry Potter</title> <price>29.99</price> <pages>200</pages> <released>2001-01-01</released> </book> <book> <title lang="eng">Learning XML</title> <price>39.95</price> <pages>100</pages> <released>2003-01-01</released> </book> <book> <title lang="eng">Learning JSON</title> <price>49.95</price> <pages>22</pages> <released>2003-01-01</released> </book> <cd> <title lang="eng">The Downward Spiral</title> <price>39.95</price> <released>2009-01-01</released> </cd> </bookstore>

    The primary place XPath2 is updated (at least from what I can tell) is in terms of all the functions that are supported. So for example, you can do a date function search like so:

    <cfset booksAfter2002 = xmlSearch(xmlDoc,"/bookstore/book[year-from-date(released) > 2002]")> <cfdump var="#booksAfter2002#" label="Books after 2002">

    You can also do interesting math on the XML:

    <cfset minpages = xmlSearch(xmlDoc,"/bookstore/book[pages=min(/bookstore/book/pages)]")> <cfdump var="#minpages#" label="Min Pages"> <cfset maxprice = xmlSearch(xmlDoc,"/bookstore/book[price=max(/bookstore/book/price)]")> <cfdump var="#maxprice#" label="Max Price"> <cfset avgprice = xmlSearch(xmlDoc,"avg(/bookstore/book/price)")> <cfoutput>Average price: #dollarFormat(avgprice)#<p></cfoutput>

    The first returns the book with the fewest pages, the second the one with the highest price. They both return the full XML node so you have access to the entire block of data. The last one returns an average price for the books: $39.96

    How about regular expressions? That works too:

    <cfset regex = xmlSearch(xmlDoc,"/bookstore/book[matches(title,""L*arning"")]")> <cfdump var="#regex#" label="Regex">

    Sorry that isn't the best regex in the world, but you get the idea. Finally, you can even build expressions that take attributes:

    <cfscript> params = {"test"="cd"}; </cfscript> <!--- find all nodes with element name as passed by variable test ---> <cfset result = xmlsearch(xmldoc,"/bookstore/*[local-name() eq $test]", params)> <cfdump var="#result#">

    You get the idea. Please note the XSLT has also been updated. Frankly I don't know enough about XSLT 2 to comment on this, but overall, it's a good improvement to ColdFusion's built in XML support.

Raymond Camden's Picture

About Raymond Camden

Raymond is a developer advocate. He focuses on JavaScript, serverless and enterprise cat demos. If you like this article, please consider visiting my Amazon Wishlist or donating via PayPal to show your support. You can even buy me a coffee!

Lafayette, LA https://www.raymondcamden.com

Comments