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.
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.
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:
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:
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.)
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.
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?
<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')>
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.
http://www.anujgakhar.com/2007/10/31/using-xpath-w...
This is my version.
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.
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.
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?
function-exists('lower-case') in an xpath expression.
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
It should be function-available.
e.g.
<cfset isFunction = XmlSearch(myXml, function-available(lower-case)) />
read the last comment on this blog post here
http://www.anujgakhar.com/2007/10/31/using-xpath-w...
<cfsavecontent variable="XMLFile">
<ProductsComparison xmlns="http://com.etilize.spexlive">
<products>
<productSummary id="11964813">
<category parId="10502" name="Mailers & 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 & 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
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 & Envelopes" id="10500"/>
<manufacturer sku="75050" name="MeadWestvaco" id="1020728"/>
Thanks again,
Edith
<cfset ProductNodes = xmlSearch(MyXMLDoc,"/*[local-name()='ProductsComparison' and namespace-uri()='http://com.etilize.spexlive']/*[local-name()='products']/*[local-name()='productSummary']")>
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>
<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
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...
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
<!--- Setup some XML to work with --->
<cffile action="read" file="C:Temp est2.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>
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.
<?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
Thanks so much for taking the time to put this whole thing together.
You've REALLY helped me out!!!!
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.
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!