Coldfusion XML to Struct

On November 5, 2007, in ColdFusion, by Anuj Gakhar

XML parsing in Coldfusion has improved a lot in the last few versions but converting a complex XML object to a Coldfusion structure is still a struggle I beleive. There are a few custom tags out there for doing this but the ones that I have used or know are all only for simple structures, not nested or subnested structures. So I use this little function I and one of my other friends worked on and it works like a charm.

Update (27/12/2008) : The new code has been upload on riaforge and the usage is now very simple . It now takes the XML file and converts to Struct without doing all the Xpath’s before the function call. Here is a sample usage.

[xml]

[/xml]

The new code is here as well – Xml2Struct CFC

Anything below is now outdated :-

Update : Project can now be downloaded from here

A sample usage of the fucntion is below :-

Read the XML file into a Coldfusion variable, this can be a from a file locally or via a CFHTTP call or a webservice call.

[xml]


You need to know the root element of the XML document and pass that to the function.
[/xml]

Then call the function to convert it to a Structure.

[xml]
[/xml]

The function itself can be found here.

The only problem that I notice about this is if the root element has any default namespaces (“xmlns=’blabla’) , the XmlSearch doesnt recognise it. Apart from that, it works like a charm.

The XML file used in this sample can be found here books sample xml file .

Screen Dump of OutputHere is the output of the code.

Tagged with:  

87 Responses to Coldfusion XML to Struct

  1. Radek says:

    Oh man! I remember that. This is great piece of code. Its a shame we can’t say where this was used 🙁 Anuj – I think its worth of placing this code @ riaforge.org.

  2. admin says:

    I agree Radek. I am gonna polish it a bit and make it a CFC and put it up on Riaforge.

  3. radek says:

    So this is additional some docs for this function. When you have more than one book in catalogue the function will create array of structs in book struct (as you can see on dump). When you have just one book the function will create struct in struct (without an array). So you must check if book is an array or struct. If its an array it means you have more than one book.
    There is also another feature here which isn’t preented on the screen shot. If you have something like:

    John Locke

    you’ll get struct with these keys:

    – author
    – author_arguments

    First item will be node value, second item will be struct where keys are arguments names and values are argments values.
    Anuj – correct me if i’m wrong.

  4. radek says:

    In above comment, instead of John Locke should be:

    <author lang="en_GB">John Locke</author>

  5. admin says:

    thats correct Radek. If there are any attributes to any XML element, the outupt look like it is here. https://www.anujgakhar.com/wp-content/uploads/2007/11/screenshot002.jpg

    So the only rule is to know the root element of the XML document and this fucntion does the rest !

  6. todd sharp says:

    I guess I’m not sure why you’d do this? Isn’t it just as easy to use the raw XML? Why go to the extra step of converting to a struct/array of structs?

  7. Anuj Gakhar says:

    I guess XML parsing is OK when you know the structure of the XML. The reason I initially worked on this was because one of the webservice calls I made was returning an unpredicatable and heavily nested XML back and it was hard to work with that. Thats how I ended up with this function. Apart from that, its a nice thing to be able to convert to a structure. 🙂

  8. Steven says:

    Could you give an example of how you would do a cfhttp call? I am not sure how you would define the variable then.

  9. Steven says:

    One more thing can you show an example of how you would display the data for instance if you didnt want to do a dump you just wanted to display the book title.

  10. Anuj Gakhar says:

    Steven, in a CFHTTP call , you would have something like :-
    <cfset myXml = XmlParse(cfhttp.fileContent) />
    and once you have done this
    <cfset allBooks = “#ConvertXmlToStruct(responseBody[1], structnew())#”>
    you can do
    <cfset firstTitle = allBooks[1].title />

  11. Rahul Narula says:

    Hi Anuj
    Nice post.

    I feel we can also do away with the requirement of knowing the root element by dynamically getting the root element as :

    instead of:

    mentioned in your example.

    Though we can only use structKeyList(myXML) to get the root element as root element will always be a single element but I just put the listfirst to be doubly sure.

  12. Rahul Narula says:

    Hi Anuj
    Nice post.

    I feel we can also do away with the requirement of knowing the root element by dynamically getting the root element as :
    <cfset xmlBody= xmlSearch(myXml,”//#listfirst(structKeyList(myXML))#”)>

    instead of:
    <cfset xmlBody= xmlSearch(myXml,”//catalog”) />
    mentioned in your example.

    Though we can only use structKeyList(myXML) to get the root element as root element will always be a single element but I just put the listfirst to be doubly sure.

  13. Anuj Gakhar says:

    @Rahul, After I posted it, I figured that we can actually find the root node dynamically by doing this.
    <cfset xmlBody= xmlSearch(myXml,”./node()”)>

    Thats a better way of doing it, i have an updated vrsion of this code but just didnt get the time to post it here.

  14. Rahul Narula says:

    ya cool, xPath is even better

  15. Anuj Gakhar says:

    ya I quite like Xpath…

  16. Wikiwikiman says:

    Will this function work correctly in CF MX 6.1? I don’t seem to get it right, no matter what I try…

  17. Anuj Gakhar says:

    @WikWikiman,
    I have tested this only on CF 7 and 8 but I cant see a reason off the top of my head why it would not work in 6.0.
    can you point out the error you are getting ?

  18. Wikiwikiman says:

    @Anuj,
    I’m not at work at this moment; I’ll post some code tomorrow. Essentially, it seemed as if I was always passing in the wrong type of XML document…

    Just to put things straight: shouldn’t your example in the original post be like this: ConvertXmlToStruct( XmlBody, structnew()) ? (I fail to see where CF would look to find responseBody[1]…)

  19. Anuj Gakhar says:

    @Wikiwikiman,

    I know, the example should have been better. I have been trying to get some time to update this post with some better example. I will do it this week. But you are free to send me the code and i can surely point you in the right direction.

  20. Wikiwikiman says:

    So here’s my code (CFMX 6.1):

    If I now try this:

    then I get this error message:

    >> You have attempted to dereference a scalar variable
    >> of type class java.lang.String as a structure with members.

    That’s logical: a String does not have ‘XmlChildren’. So I need to pass in an XML document object (you could add a check for that in the function, by the way, using the IsXmlDoc() function). But when I try this:

    then I get another error message:

    >> Element XMLCHILDREN is undefined in AXML

    pointing to line 17 in your function…

  21. Wikiwikiman says:

    OUCH – second attempt…

    So here’s my code (CFMX 6.1) for real, I hope:

    <cffile action=”read” file=”#somexmlfile#” variable=”filecontent” />
    <cfset parsed = XmlParse( filecontent ) />

    If I now try this:

    <cfset datainastruct = ConvertXmlToStruct( filecontent, StructNew() ) />

    then I get this error message:

    >> You have attempted to dereference a scalar variable
    >> of type class java.lang.String as a structure with members.

    That’s logical: a String does not have ‘XmlChildren’. So I need to pass in an XML document object (you could add a check for that in the function, by the way, using the IsXmlDoc() function). But when I try this:

    <cfset datainastruct = ConvertXmlToStruct( parsed, StructNew() ) />

    then I get another error message:

    >> Element XMLCHILDREN is undefined in AXML

    pointing to line 17 in your function…

  22. Anuj Gakhar says:

    You need to add another line in there.

    <cffile action=”read” file=”#somexmlfile#” variable=”filecontent” />
    <cfset parsed = XmlParse( filecontent ) />
    <cfset responseBody = xmlSearch(parsed ,”/node()”) />
    <cfset datainastruct = ConvertXmlToStruct( responseBody[1], StructNew() ) />

    As I said in the post, it needs the root element of the XML, I am going to have to do all of this in the function itself ideally.

    let me know how it goes.

  23. Wikiwikiman says:

    OK, now we’re talking – even in CFMX 6.1. Thanks for the helping hand.

    Allow me to make a suggestion: why not change the function so that it takes the raw string data (from a file or whatever) as input and returns the Struct as output? That way, all the special preparation is where it belongs: in the function itself. As a developer, I hand over the XML stream and I get my CF struct in return for further manipulation… Easy to remember and easy to do.

  24. Anuj Gakhar says:

    yeah I agree to that and I have just now updated the project code at http://xml2struct.riaforge.org/

    The function now just takes a XML file and converts to Struct. Like it should have from the beginning.

  25. Anuj Gakhar says:

    Updated this post as well for future readers.

  26. Bruce Holm says:

    I’m using CFMX 6.1 and still getting the latest .cfc to work for me. The failure is on line 18 of your .cfc and the error is:

    Document root element is missing. Document root element is missing.
    The error occurred on line 18.

    I’m not sure how to fix this.

  27. Wikiwikiman says:

    @Bruce: I recall having seen that same message… but I can’t remember how I got it to disappear ;-(

    Make sure that you are not calling the XML parsing and conversion code within a CFOUTPUT section; make sure that your XML is correct, well-formed and valid – the CF XML parser is quite strict and intolerant.

  28. Bruce Holm says:

    I have looked into the .cfc and find that the most recent posting has a problem. The first time the function is called it passes in a string which is correct. It is the xml text file read into a variable that is passed. But the two recursive calls to the same function inside the function both appear to pass an XML object. This is what I believe is causing the error. I have modified the code in my local copy so that in both cases it passes in an array. I moved the ParseXML outside the function for the original call. This at least gets around the error.

    I now have a new question on this code. Does it transfer parameters for xml tags into the struct? It doesn’t appear to do so. For example the catalog/book example XML has an id parameter on the book tag () but I don’t find that value anywhere in the struct produced. Is this intended?

  29. Anuj Gakhar says:

    @Bruce, Thanks for pointing it out. At the moment, the function doesnt do anything with the attributes of the root XML node. Any attributes in the child nodes will be picked up. I will have to modify the code to include that functionality. I will give it a go once I get some free time.

  30. Bruce Holm says:

    Each iteration of the recursive call to the function changes the child nodes to become the root XML node which means none of the attributes are picked up. But maybe I misunderstand.

  31. Anuj Gakhar says:

    @Bruce, In the example books.xml try adding attributes to one of the description nodes or title nodes , they do get picked up.

  32. Bruce Holm says:

    So does it only pick up attributes from the bottom level nodes? Given that the code “walks” down through the hierarchy I would guess that it does not pick up the top and middle level node attributes.

  33. Anuj Gakhar says:

    http://pastebin.com/f166d90f9

    I have pasted some code here
    line 56-68 are new lines. What was happening is , for any repeating nodes, (in this example, “book” node) the attributes were ignored. but all child node attributes are picked up. Now this code, creates a new _attributes key in the resulting structure that takes care of this issue.

    @Bruce, can you test this now ?

  34. Rahul Narula says:

    To overcome the namespace issue you might want to try this http://tinyurl.com/2c3fda

  35. Anuj Gakhar says:

    @Rahul, Yes I am aware of that. I wrote about this in this post.
    https://www.anujgakhar.com/2007/11/21/extracting-links-using-xpath/

  36. Michaela Szidloski says:

    Hi! I keep getting an error on line 17

    Premature end of file.

    Here’s my code:

    the file (#application.xmltemplateroot_phy##XMLData#) is in a query output b/c I have many xml files I need converted to coldfusion structures. Any help would be greatly appreciated.

  37. Michaela Szidloski says:

    Hi! It looks like my code didn’t go through:

    Here’s my code:

    Thanks again!

  38. Michaela Szidloski says:

    I’m not sure why when I copy and paste the error the line of code doesn’t display. I will type it out.
    This is the line it errors on with Premature end of file:

    cfset axml = XmlSearch(XmlParse (arguments.xmlNode, “/node()”)

    Let’s see if this comes through.

  39. Michaela Szidloski says:

    Okay here the other code:

    cffile action=”read” file=”#application.xmltemplateroot_phy##XMLData#” variable=”filecontent”

    cfset objXml = createObject(“component”,”xml2Struct”)

    cfset myXml = XmlParse(filecontent)

    cfset responseBody = xmlSearch(myXml,”/node()”)

    cfset datainastruct = objXml.ConvertXmlToStruct( responseBody[1], StructNew() )
    cfdump var = “#objXml.ConvertXmlToStruct(responseBody[1], structnew())#”

    Thanks again!

  40. Anuj Gakhar says:

    @Michaela, it looks like you are not using the latest version of the CFC, also, what version of CF are you on?

    you might want to me email me offpost and I will be glad to help…more details on the contact page.

  41. Micah says:

    The current version up is broken. I fixed it by adding .toString() to axml.XmlChildren[i] in the recursive calls on lines 40 & 54.

  42. Anuj Gakhar says:

    @micah, although I havent tested it out yet, but thanks for posting the fix here.

  43. Micah says:

    I am using CF 6.1 if that makes any difference. Its probably a bad solution though since it causes the xml to be converted back and forth between its string
    and XML document object representations. For some reason xmlObj.xmlChildren[1] is not of the XML document object type so that makes the obvious solution of just passing the children recursively as XML document objects not work.

  44. Sergio says:

    Hi. This is a terrific piece of code.
    But I must modify it in some way, because I’m dealing with a problem that seems impossible to solve using the code “as it is”. It is also a nice challenge to think over.
    THe problem is that the XML must be a “well formed” compliant document or the code will fail with Error 500 behaviour.
    So some Try-Catch traps must be included to degrate gracefully.
    I don’t know where to put them, but I can provide a file that crashes the code.
    If you are interested just drop me a line.

  45. Anuj Gakhar says:

    @Sergio, you can simply check for the validity of the XML before you even call the function. Its best to keep it outside the function I guess.

  46. Travis May says:

    Do you have any idea how awesome you are????

    FYI, the link is broken, I found on RIAFORGE
    https://www.anujgakhar.com/wp-content/uploads/2008/02/xml2struct.cfc.txt

  47. Travis May says:

    I should elaborate. I spent time trying to figure out hot to stop AXIS from adding XsiTypes to request elements that was causing the 3rd party web service to fail. I didn’t want to try to parse out all of this XML. So, I just formulate the SOAP request manually and use your nifty xmlToStruct function to slam the entire thing into a nice little struct.

    Thanks !!!!
    Travis

  48. Anuj Gakhar says:

    @Travis, thanks for the appreciation. I am glad the function was useful to you. Cheers.

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.

© 2011 Anuj Gakhar
%d bloggers like this: