There are many, many uses for XML, and if you use ColdFusion to consume web services, or create data integration processes, then chances are, you will need work with XML. As of ColdFusion MX there are several tags and functions that make working with XML a snap. In this tutorial I am going to focus on teaching you how to pull the information you need out of XML documents.

There are several ways you might use to read the XML text into a ColdFusion variable. Which one you use depends on the source of the XML. If it's in a database you would use CFQUERY, if it is on a file on the ColdFusion server you would read it with CFILE, if you are downloading it from the web you could use CFHTTP or CFFTP, etc. Ultimately, you will end up with a ColdFusion variable that contains the XML text. For this example, and just to keep it simple, I am going to use CFSAVECONTENT to create that variable. Rather than including this CFSAVECONTENT code in each of my code samples, you can assume that all the XMLFile variables contain this XML.

<cfsavecontent variable="XMLFile"><?xml version="1.0"?>
<books>
<book>
          <isbn>0321330110</isbn>
<title>Macromedia ColdFusion MX 7 Certified Developer Study Guide</title>
<author>Ben Forta</author>
</book>
    <book>
          <isbn>0596004206</isbn>
<title>Learning XML, Second Edition</title>
<author>Erik Ray</author>
</book>
    <book>
          <isbn>0782140297</isbn>
<title>Coldfusion MX Developer's Handbook</title>
<author>Raymond Camden</author>
</book>
</books>
</cfsavecontent>

As you can see, we are going to work with XML that contains a list of books. The first thing we need to do is use XMLParse() to convert the text contained in the "XMLFile" variable into a ColdFusion XML document object. An XML document object is how ColdFusion represents an XML document, and is much like a ColdFusion structure.

<cfset MyXMLDoc = xmlParse(XMLFile)>

<cfdump var="#MyXMLDoc#">

If you run the above code you will get the following output:

Now you are at the point where you can start pulling the data that you need out of the XML. The best way to pull the information you need out of a ColdFusion XML document object is to use the XMLSearch() function. XMLSearch uses an XPath language expression to search an XML document object and returns a ColdFusion array containing the matching nodes. XPath is a language for addressing parts of an XML document, and the expressions are pretty easy to write once you spend a little time figuring them out.

The basic XPath syntax is similar to file system addressing. If the path starts with a "/", then it represents an absolute path to the element you want.

So if we want to get all the "book" nodes from our XML we could use:

<cfset MyXMLDoc = xmlParse(XMLFile)>

<cfset BookNodes = xmlSearch(MyXMLDoc,'/books/book')>

<cfoutput>
<cfloop from="1" to="#arraylen(BookNodes)#" index="i">
   <!--- The array contents need to parsed so you can easily get at
      the child nodes children and attributes. --->

   <cfset BookXML = xmlparse(BookNodes[i])>
   <b>ISBN:</b> #BookXML.book.isbn.xmltext#<br>
   <b>Title:</b> #BookXML.book.title.xmlText#<br>
   <b>Author:</b> #BookXML.book.author.xmlText#<br><br>
</cfloop>
</cfoutput>

If the path starts with "//" then all elements in the document which fulfill criteria following the "//" are selected. So, if we just wanted to pull a list of authors from our XML, we could use:

<cfset MyXMLDoc = xmlParse(XMLFile)>

<cfset Authors = xmlSearch(MyXMLDoc,'//author')>
<cfoutput>
<cfloop from="1" to="#arraylen(Authors)#" index="i">
   <cfset AuthorXML = xmlparse(Authors[i])>
   <b>Author:</b> #AuthorXML.author.xmlText#<br><br>
</cfloop>
</cfoutput>

With more advanced XPath expressions you can do things like search for nodes with specific values. Here is an example of how to find all the books with titles that contain "ColdFusion". (Keep in mind that these contains() searches are case sensitive.)

<cfset MyXMLDoc = xmlParse(XMLFile)>

<cfset SearchByTitle = xmlSearch(MyXMLDoc,'//book [contains(title,"ColdFusion")]')>
<cfoutput>
<cfloop from="1" to="#arraylen(SearchByTitle)#" index="i">
   <cfset BookXML = xmlparse(SearchByTitle[i])>
   <b>ISBN:</b> #BookXML.book.isbn.xmltext#<br>
   <b>Title:</b> #BookXML.book.title.xmlText#<br>
   <b>Author:</b> #BookXML.book.author.xmlText#<br><br>
</cfloop>
</cfoutput>

There are many other ways you can use XMLSearch and XPath to pull the data you need from your XML. To learn more about them visit http://www.w3.org/TR/xpath, or just search Google for "XPath Tutorial" and you will find several good ones.

Related Blog Entries

Comments (Comment Moderation is enabled. Your comment will not appear until approved.)
another cowardly anon's Gravatar Thanks for the post. I'm having a lot of trouble reading XML files. You would think it would be easy but for some reason nothing seams to work. Not the adobe pdf on reading XML coldfusion, not easyCFM and I'm having trouble with yours.

Simply copying and pasting works!!! Woo Hoo. The first copy and paste that works.
(I'm wondering do DTD's affect reading XML?)

So now, I place your savecontent into an external xml file called books.xml.

I call it

<cfhttp url="http://127.0.0.1/test/xml/books.xml"; method="GET">
<cfset xmlfile = xmlparse(cfhttp.filecontent)>
<cfset MyXMLDoc = xmlParse(xmlfile)>


<cfdump var="#MyXMLDoc#">

and it works. However, when I copy and paste the next part nothing is displayed.

<cfset MyXMLDoc = xmlParse(XMLFile)>

<cfset BookNodes = xmlSearch(MyXMLDoc,'/books/book')>

<cfoutput>
<cfloop from="1" to="#arraylen(BookNodes)#" index="i">
<!--- The array contents need to parsed so you can easily get at
the child nodes children and attributes. --->
<cfset BookXML = xmlparse(BookNodes[i])>
<b>ISBN:</b> #BookXML.book.isbn.xmltext#<br>
<b>Title:</b> #BookXML.book.title.xmlText#<br>
<b>Author:</b> #BookXML.book.author.xmlText#<br><br>
</cfloop>
</cfoutput>


The author code displays well. What's up?
# Posted By another cowardly anon | 11/22/07 4:45 PM
Scott Bennett's Gravatar If you are going to get your XML file via http you would do it like this:

<cfhttp url="http://127.0.0.1/test/xml/books.xml"; method="GET">
<cfset MyXMLDoc = xmlParse(cfhttp.filecontent)>

<cfset BookNodes = xmlSearch(MyXMLDoc,'/books/book')>

<cfoutput>
<cfloop from="1" to="#arraylen(BookNodes)#" index="i">
<cfset BookXML = xmlparse(BookNodes[i])>
<b>ISBN:</b> #BookXML.book.isbn.xmltext#<br>
<b>Title:</b> #BookXML.book.title.xmlText#<br>
<b>Author:</b> #BookXML.book.author.xmlText#<br><br>
</cfloop>
</cfoutput>


Also you need to keep in mind that XMLSearch is case sensitive. So if you changed the case of any of the XML that I provided then the XMLSearch will return no results. If you changed the <books> node to <BOOKS> for example, then you will need to make sure that this line:

<cfset BookNodes = xmlSearch(MyXMLDoc,'/books/book')>

is changed to

<cfset BookNodes = xmlSearch(MyXMLDoc,'/BOOKS/book')>
# Posted By Scott Bennett | 11/23/07 1:25 PM
another cowardly anon's Gravatar Sorry for taking so long to respond and thanks for the answer unfortunately I'm missing something. I really don't know why I'm having trouble this seems so easy.

Things still don't work.

This time the error is the ";" that's in
<cfhttp url="http://127.0.0.1/test/xml/books.xml";; method="GET">

(Is that really supposed to be there?)

Here's the XML page: books.xml

<xml version="1.0">
<books>
<book>
<isbn>0321330110</isbn>
<title>Macromedia ColdFusion MX 7 Certified Developer Study Guide</title>
<author>Ben Forta</author>
</book>
<book>
<isbn>0596004206</isbn>
<title>Learning XML, Second Edition</title>
<author>Erik Ray</author>
</book>
<book>
<isbn>0782140297</isbn>
<title>Coldfusion MX Developer's Handbook</title>
<author>Raymond Camden</author>
</book>
</books>
</xml>

and the CFM page you know:

<cfhttp url="http://127.0.0.1/test/xml/books.xml";; method="GET">
<cfset MyXMLDoc = xmlParse(cfhttp.filecontent)>

<cfset BookNodes = xmlSearch(MyXMLDoc,'/books/book')>

<cfoutput>
<cfloop from="1" to="#arraylen(BookNodes)#" index="i">
<cfset BookXML = xmlparse(BookNodes[i])>
<b>ISBN:</b> #BookXML.book.isbn.xmltext#<br>
<b>Title:</b> #BookXML.book.title.xmlText#<br>
<b>Author:</b> #BookXML.book.author.xmlText#<br><br>
</cfloop>
</cfoutput>


It doesn't seem complicated so I'm really frustrated by this not working.
# Posted By another cowardly anon | 12/13/07 6:27 PM
Scott Bennett's Gravatar the semi-colon should not be there, that is showing up because of a glitch in the blog.cfc comments functionality, I should have made a note of that.
# Posted By Scott Bennett | 12/14/07 12:19 PM
Anuj Gakhar's Gravatar Nice article. However, it seems we both seem to have demonstrated on the same subject with a similar example.
http://www.anujgakhar.com/2007/10/31/using-xpath-w...

This is my version.
# Posted By Anuj Gakhar | 12/28/07 7:36 AM
Scott Bennett's Gravatar @Anuj,

If you are trying to imply that I got the idea to write this article from reading yours, then I assure you that is not the case. I have never seen your article before today. I chose to use XML related to books because I was writing this article to be published on http://tutorial18.learncf.com/, and their requirements are that all examples are to be written using the "cfbookclub" example database that comes with ColdFusion. I knew I was going to be writing a second article as well (http://tutorial20.learncf.com/) that would pull from the cfbookclub database to generate an xml document, and I wanted there to be a little consistency between the two articles.

Beyond the use of a book related XML structure and the general topic of using xPath expressions, our articles differ quite a bit in the intended audience. Mine is geared as more of an introduction to xPath expressions and how to use them to parse the information you need from an XML document and it hints at the end that there is much more you can do with xPath expressions.

Your article seems to be geared more toward developers who already know a little bit about xPath expressions as it doesn't really explain what xPath expressions are and you expect your audience to be able to figure it out for themselves by reading the code. Your article shows more advanced ways to use xPath, which I must admit I found quite interesting because I had never thought of using the count() or sum() functions the way you have demonstrated there. Pretty neat stuff. Thanks.
# Posted By Scott Bennett | 12/28/07 12:35 PM
Anuj Gakhar's Gravatar Scott,

You got me wrong. I was not trying to say that your article had aanything
to do with mine, all I was saying is we ended up writing on a similar subject.

And we are all sharing knowledge so why bother anyways.

Cheers. keep up the good work.
# Posted By Anuj Gakhar | 12/28/07 3:26 PM
Scott Bennett's Gravatar Ok that's cool, sorry for the misunderstanding. =)
# Posted By Scott Bennett | 12/28/07 4:45 PM
Matthew's Gravatar Have you used the lower-case() along with contains() to make the contains not case sensitive? When i try it it says the lower-case() is not found.

something like

xmlSearch(myxml,"//child[contains(.,lower-case('searchString'))]")

returns an error

Could not find function: lower-case

any suggestions are appreciated. Is this not available in the java behind xpath searches in cf?
# Posted By Matthew | 4/10/08 2:56 PM
Scott Bennett's Gravatar From my testing I am concluding that the lower-case() function was not implemented in the ColdFusion xPath implementation. I tried to find some documentation to verify that but can't find anything. I may dig a little deeper later if I have time but for now I would say it is just not implemented.
# Posted By Scott Bennett | 4/22/08 7:09 PM
Anuj Gakhar's Gravatar you can check by doing this
function-exists('lower-case') in an xpath expression.
# Posted By Anuj Gakhar | 4/22/08 7:15 PM
Scott Bennett's Gravatar @Anuj,

just tired that and I get a similar error to the one that Matthew got for the lower-case function:

Could not find function: function-exists
# Posted By Scott Bennett | 4/22/08 8:35 PM
Anuj Gakhar's Gravatar Sorry, my bad.

It should be function-available.

e.g.
<cfset isFunction = XmlSearch(myXml, function-available(lower-case)) />
# Posted By Anuj Gakhar | 4/23/08 9:05 AM
Anuj Gakhar's Gravatar it seems i cant put double quotes in code here.....
read the last comment on this blog post here
http://www.anujgakhar.com/2007/10/31/using-xpath-w...
# Posted By Anuj Gakhar | 4/23/08 9:07 AM
Scott Bennett's Gravatar function-available worked, and it returned a 'No' so obviously that means lower-case was not implemented in ColdFusion xPath implementation. Thanks for that tip Anuj!
# Posted By Scott Bennett | 4/23/08 1:45 PM
Edith's Gravatar Hi Scott, I have this situation:
<cfsavecontent variable="XMLFile">
<ProductsComparison xmlns="http://com.etilize.spexlive";>" target="_blank">http://com.etilize.spexlive";>;
<products>
<productSummary id="11964813">
<category parId="10502" name="Mailers &amp; Envelopes" id="10500"/>
<manufacturer sku="75050" name="MeadWestvaco" id="1020728"/>
<descriptions>
<pd t="2">MeadWestvaco Plain Business Size Envelopes</pd>
<pd t="3">Business Envelope - #104.12" x 9.5" - Self-sealing - 50 Pack - White</pd>
<pd t="1">MeadWestvaco Plain Business Size Envelopes - #10 (4.12" x 9.5") - Self-sealing - 50 Pack - White</pd>
</descriptions>
</productSummary>
<productSummary id="11967515">
<category parId="10502" name="Mailers &amp; Envelopes" id="10500"/>
<manufacturer sku="11118" name="Quality Park Products" id="102086"/>
<descriptions>
<pd t="2">Quality Park Redi-Seal Business Envelopes</pd>
<pd t="3">Business Envelope - #104.12" x 9.5" - 24lb - Self-sealing - 500 Pack - White</pd>
<pd t="1">Quality Park Redi-Seal Business Envelopes - #10 (4.12" x 9.5") - 24lb - Self-sealing - 500 Pack - White</pd>
</descriptions>
</productSummary>
</products>
</ProductsComparison>
</cfsavecontent>
<cfset MyXMLDoc = xmlParse(XMLFile)>

<cfset ProductNodes = xmlSearch(MyXMLDoc,'/ProductsComparison/products/productSummary')>
<cfset AttributeNodes = xmlSearch(MyXMLDoc,'/ProductsComparison/dataSheetComparison/attributeGroup/attribute')>         

<cfoutput>#arraylen(ProductNodes)#xxx#arraylen(AttributeNodes)#yyy
<cfloop from="1" to="#arraylen(ProductNodes)#" index="i">
<!--- The array contents need to parsed so you can easily get at
the child nodes children and attributes. --->
<cfset ProductXML = xmlparse(ProductNodes[i])>
<b>PD:</b> #ProductXML.productSummary.descriptions.pd.xmlText#<br><br>
</cfloop>
</cfoutput>

That by itself won't work and I figured out why, because by root has this xmlns="http://com.etilize.spexlive"; on it. How do I handle this?

Thanks,
Edith
# Posted By Edith | 4/25/08 4:46 PM
Edith's Gravatar Hi Scott, regarding the post above...

Please disregard the lines:
1) target="_blank"... (I think this got added when I clicked POST)
2) <cfset AttributeNodes = xmlSearch(MyXMLDoc,'/ProductsComparison/dataSheetComparison/attributeGroup/attribute')>
and
3) #arraylen(AttributeNodes)#yyy, #2 and #3 belong to the rest of the XML text that I deleted.

Follow-up question:
How do I get the values of the attributes within the productSummary/category and productSummary/manufacturer, see below:
<category parId="10502" name="Mailers &amp; Envelopes" id="10500"/>
<manufacturer sku="75050" name="MeadWestvaco" id="1020728"/>

Thanks again,
Edith
# Posted By Edith | 4/25/08 4:58 PM
Scott Bennett's Gravatar When you have a namespace assigned to your XML it gets a little more complicated. You have to use the local-name() and namespace-uri() functions to identify the nodes in your xPath expression. Here is how you would retrieve your Product Nodes:

<cfset ProductNodes = xmlSearch(MyXMLDoc,"/*[local-name()='ProductsComparison' and namespace-uri()='http://com.etilize.spexlive']/*[local-name()='products']/*[local-name()='productSummary']")>
# Posted By Scott Bennett | 4/25/08 6:26 PM
Scott Bennett's Gravatar @Edith,

Also, after you do the search above, you can access the attributes in your xml nodes like this:

<cfoutput>
<cfloop from="1" to="#arraylen(ProductNodes)#" index="i">
<cfset ProductNodeXML = xmlparse(ProductNodes[i])>
<cfdump var="#ProductNodeXML#">
<b>Category ParID:</b> #ProductNodeXML.productsummary.category.XmlAttributes.parId#<br>
<b>Category Name:</b> #ProductNodeXML.productsummary.category.xmlAttributes.name#<br>
</cfloop>
</cfoutput>
# Posted By Scott Bennett | 4/25/08 6:49 PM
Edith's Gravatar Thanks Scott, I appreciate that you reply to my questions. the local-name() and the namespace-uri() worked. I do have another question though... How do I deal with SOAP responses? Instead of parsing a simple XML, my XML is now within soap tags, please see below:
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"; xmlns:xsd="http://www.w3.org/2001/XMLSchema"; xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">...;
<soap:Body>
...same XML goes here...
</soap:Body>
</soap:Envelope>

Thanks in advance,
Edith
# Posted By Edith | 4/25/08 9:03 PM
Scott Bennett's Gravatar @Edith,

Ben Nadel wrote an article a while back that demonstrates how to remove the prefixes and namespaces from SOAP XML to make it easier to use xPath expressions, you may want to give that a try.

http://www.bennadel.com/blog/494-Stripping-XML-Nam...
# Posted By Scott Bennett | 4/28/08 8:20 PM
renu's Gravatar Hi,
I m having a problem in reading the xml file. the structure of xml file looks like <company name="Macromedia">
   <employees><person>Tom Cruise</person><age>20</age></employees>
<employees><person><h1>John </h1></person><age>20</age></employees></company>
in this xml file person node has HTML tag. i need to read the xml file data and populate them to form fields. Can anyone help me on this.
Thanks Renu
# Posted By renu | 1/21/09 3:42 PM
renu's Gravatar One more thing i forgot to write, i need to update/ add and delete the data from xml file
# Posted By renu | 1/21/09 3:45 PM
Tutorials Men's Gravatar Thanks for tutorial, 1 exemple is more than 1000 words
# Posted By Tutorials Men | 4/21/09 3:10 PM
Dave's Gravatar I tried your example using CF MX6.1 (I know....) and it didn't work. I had to make several modifications as can be seen below, have the functions changed. When I ran your version I kept getting a 'Document root element is missing' error on <cfset BookXML = xmlparse(BookNodes[i])>, I guess because we had already stripped this out from Books. Did these functions change to become more user friendly, will my code still work under MX 8 so I don't have to rewrite it when I upgrade (hopefully soon).

<!--- Setup some XML to work with --->
<cffile action="read" file="C:\Temp\test2.xml" variable="XMLFile">

<!--- Parse the XML --->
<cfset MyXMLDoc = xmlParse(XMLFile) />

<!--- Dump the XML --->
<h2>Dump</h2>
<cfdump var="#MyXMLDoc#">

<!--- Get all Book Nodes --->
<cfset BookNodes = xmlSearch(MyXMLDoc,'/books/book')>
<cfoutput>
No of Books: #arraylen(BookNodes)#
   <h2>Book Nodes</h2>
   <cfloop from="1" to="#arraylen(BookNodes)#" index="i">
    <!--- The array contents need to parsed so you can easily get at the child nodes children and attributes. --->
<!--- <cfset BookXML = xmlparse(BookNodes[i])> --->
    <cfset BookXML = BookNodes[i] />
    <b>ISBN:</b> #BookXML.isbn.xmltext#<br>
    <b>Title:</b> #BookXML.title.xmlText#<br>
    <b>Author:</b> #BookXML.author.xmlText#<br><br>
   </cfloop>
</cfoutput>

<!--- Get all Author nodes --->
<cfset Authors = xmlSearch(MyXMLDoc,'//author')>
<cfoutput>
   <h2>Author Nodes</h2>
   <cfloop from="1" to="#arraylen(Authors)#" index="i">
    <cfset AuthorXML = Authors[i] />
    <b>Author:</b> #AuthorXML.xmlText#<br><br>
   </cfloop>
</cfoutput>

<!--- More advanced search --->
<cfset SearchByTitle = xmlSearch(MyXMLDoc,'//book [contains(title,"ColdFusion")]')>
<cfoutput>
   <h2>Advanced Search</h2>
   <cfloop from="1" to="#arraylen(SearchByTitle)#" index="i">
    <cfset BookXML = SearchByTitle[i]>
    <b>ISBN:</b> #BookXML.isbn.xmltext#<br>
    <b>Title:</b> #BookXML.title.xmlText#<br>
    <b>Author:</b> #BookXML.author.xmlText#<br><br>
   </cfloop>
</cfoutput>
# Posted By Dave | 6/21/09 11:55 AM
Sanky's Gravatar Please help me. i have to create a one small module into spex live integration in mageno so can u help me
# Posted By Sanky | 2/24/10 3:44 AM
Scott Bennett's Gravatar @Sanky
I have no Idea what "spex" or "mageno" is. I'd be happy to help if you can clarify you question. You can use the Contact page on this site to send me the full description.
# Posted By Scott Bennett | 2/24/10 5:43 PM
Ted Daniels's Gravatar I have a simple xml doc and cannot seem to extract just the ID Numbers

<?xml version="1.0" encoding="utf-8"?>
<ArrayOfLong xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"; xmlns:xsd="http://www.w3.org/2001/XMLSchema"; xmlns="http://copsync.com/interface">;
<long>194407</long>
<long>194409</long>
<long>232747</long>
<long>232748</long>
</ArrayOfLong>

I can list everything, but not just the ID numbers using

xmlSearch(MyXMLDoc,'//')

but cannot get it to list just the <long>ID Number</long>

Any suggestions?

Ted
PS Not sure if I am posting this twice as the first time when I clicked POST, the page just closed down
# Posted By Ted Daniels | 10/24/10 2:50 PM
Evik James's Gravatar This is the first XPath tutorial that actually seemed to be helpful. I have spent the last two hours on your page just getting the bugs worked out of my code.

Thanks so much for taking the time to put this whole thing together.

You've REALLY helped me out!!!!
# Posted By Evik James | 8/6/11 6:34 PM
Mo Hadi's Gravatar Is there a way to check for an exsitence of a file on an external server first?
If I am pulling data from say http://www.mohadi.com/myxmldata.xml,
I want to be able to see if myxmldata.xml exsists before I perform XMLParse call.
# Posted By Mo Hadi | 6/18/12 7:01 PM
Emily's Gravatar Hi,

This tutorial is very helpful. I am trying to work this tutorial into parsing a soap response, but am having issues.
Is it possible to incorporate this concept when receiving an xml soap response?

Any info would be greatly appreciated!
# Posted By Emily | 6/25/12 2:45 PM
Ryan Trask's Gravatar Great tutorial. Had it working in 5 minutes in my application. Thanks.
# Posted By Ryan Trask | 9/13/12 7:50 AM
 
Home | Blog | Portfolio | Contact | © 2001 - 2007 The ColdFusion Guy - Scott Bennett. All rights reserved.
BlogCFC was created by Raymond Camden. This blog is running version 5.9.