Ben Nadel
On User Experience (UX) Design, JavaScript, ColdFusion, Node.js, Life, and Love.
I am the chief technical officer at InVision App, Inc - a prototyping and collaboration platform for designers, built by designers. I also rock out in JavaScript and ColdFusion 24x7.
Meanwhile on Twitter
Loading latest tweet...
Ben Nadel at Scotch On The Rock (SOTR) 2010 (London) with: James Allen

Building A ColdFusion XML Document From Scratch

By Ben Nadel on
Tags: ColdFusion

This is a basic exploration of the ColdFusion XML functions and methodologies that can be used to construct a ColdFusion XML document object model from scratch. I am doing this, not because it is ground breaking, but simply because I have never done it before and it really is something that I should know how to do. Here is my exploratory code - it uses XmlNew() to generate a new XML DOM (document object model) and then other methods and properties to flesh it out:

  • <!--- Create a new XML girl object. --->
  • <cfset xmlGirl = XmlNew() />
  •  
  • <!---
  • Create a root xml node under which every
  • other XML node for this document will reside.
  • Notice that we have to create this new root
  • node in the context of our existing XML document
  • so that the document owner is properly set.
  • --->
  • <cfset xmlRoot = XmlElemNew( xmlGirl, "", "girl" ) />
  •  
  • <!---
  • Our new xml node is now owned by the girl
  • document, but it is not yet part of the DOM. We
  • need to explicitly set the root XML node of the
  • girl to point to our newly created XML node.
  • --->
  • <cfset xmlGirl.XmlRoot = xmlRoot />
  •  
  •  
  • <!---
  • Create a node to hold information about her
  • personality (ex. smart, funny, kind). This
  • will be a child of the root node.
  • --->
  • <cfset xmlPersonality = XmlElemNew( xmlGirl, "", "personality" ) />
  •  
  • <!--- Append the personality to the root. --->
  • <cfset ArrayAppend(
  • xmlRoot.XmlChildren,
  • xmlPersonality
  • ) />
  •  
  •  
  • <!---
  • Create a node to hold information about her body
  • (ex. measurements, color, etc). This will be a
  • child of the root node.
  • --->
  • <cfset xmlBody = XmlElemNew( xmlGirl, "", "body" ) />
  •  
  • <!--- Append the body to the root. --->
  • <cfset ArrayAppend(
  • xmlRoot.XmlChildren,
  • xmlBody
  • ) />
  •  
  •  
  • <!---
  • Now, that we have the basic containers down for
  • this XML document, we are going to flesh out the
  • sub structures.
  • --->
  •  
  •  
  • <!---
  • First, we are going to create some properties for
  • the personality. These are going to consist of
  • attribute-driven data values.
  •  
  • NOTE: We cannot just use the reference to xmlPersonality
  • that we generated above. This does not actually point
  • to the XML node that we need to update.
  • --->
  • <cfset xmlRoot.Personality.XmlAttributes.Fun = true />
  • <cfset xmlRoot.Personality.XmlAttributes.Happy = true />
  • <cfset xmlRoot.Personality.XmlAttributes.Nice = true />
  • <cfset xmlRoot.Personality.XmlAttributes.Generous = false />
  • <cfset xmlRoot.Personality.XmlAttributes.Evil = false />
  •  
  •  
  • <!---
  • Next, we are going to create some properties for the
  • body. These are going to consist of attribute-
  • driven data values.
  •  
  • NOTE: We cannot just use the reference to xmlBody
  • that we generated above. This does not actually point
  • to the XML node that we need to update.
  • --->
  • <cfset xmlRoot.Body.XmlAttributes.Hair = "Blonde" />
  • <cfset xmlRoot.Body.XmlAttributes.Eyes = "Brown" />
  • <cfset xmlRoot.Body.XmlAttributes.Bust = 36 />
  • <cfset xmlRoot.Body.XmlAttributes.Waist = 24 />
  • <cfset xmlRoot.Body.XmlAttributes.Hips = 36 />
  •  
  •  
  • <!--- Dump out the resultant XML document object. --->
  • <cfdump
  • var="#xmlGirl#"
  • label="xmlGirl XML DOM"
  • />

When all is said and done, here is a CFDump of the resultant ColdFusion XML document:


 
 
 

 
Creating A ColdFusion XML Document From Scratch  
 
 
 

Then, if we call ToString() on the resultand DOM, we get:

  • <?xml version="1.0" encoding="UTF-8"?>
  • <girl xmlns="">
  •  
  • <personality
  • EVIL="false"
  • FUN="true"
  • GENEROUS="false"
  • HAPPY="true"
  • NICE="true"
  • xmlns=""
  • />
  •  
  • <body
  • BUST="36"
  • EYES="Brown"
  • HAIR="Blonde"
  • HIPS="36"
  • WAIST="24"
  • xmlns=""
  • />
  •  
  • </girl>

That was relatively easy to work with. That'll do ColdFusion... that'll do.




Reader Comments

Here's an alternate method that I use a lot. I prefer this way because it helps me if I can see the tag structure.
-------------------------------------------------------------------------------

<cfsavecontent variable="xsl">
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">

<xsl:output method="xml" omit-xml-declaration="yes"/>

<xsl:strip-space elements="*"/>

<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>

</xsl:stylesheet>
</cfsavecontent>

<cfxml variable="xmlGirl>
<cfoutput>
<?xml version="1.0" encoding="UTF-8"?>
<girl>
<personality>
<evil>true</evil>
<fun>true</fun>
<generous>false</generous>
<happy>true</happy>
<nice>true</nice>
</personality>
<body>
<eyes>brown</eyes>
<hair>blonde</hair>
<hips>36</hips>
<waist>24</waist>
<bust>36</bust>
</body>
</girl>
</cfoutput>
</cfxml>

<cfcontent variable="#ToBinary(ToBase64(XMLTransform(xmlGirl,xsl)))#" type="text/xml" />

Reply to this Comment

Is there a reason why you are assigning things to a variable, and then putting them into your xml document?

Why do
1) xmlRoot = XmlElemNew( xmlGirl, "", "girl" )
2) xmlGirl.XmlRoot = xmlRoot

When you could just do
1) xmlGirl.XmlRoot = XmlElemNew( xmlGirl, "", "girl" )

??

Reply to this Comment

@Matt,

That's cool looking. I really do need to learn more about XSLT. My only issue (other than not know XSLT) was that I was trying to create the XML document without using tags... I agree that tags are WAYYYYY better as you can easily see the structure - but I have done that before. I have never used XmlNew() before this.

@Rich,

I like to break things up into steps because I find it is easier to follow the individual steps. You might lose some sense of the "overall" scope of the problem, but I like to attack the problems individually. For instance, in my example, I am performing 1) Node creation, and 2) Node setting in two steps and explaining them individually... you are doing both of those in a single line of code. It's just a personal preference, but I feel like breaking them apart is slightly better for teaching because it's less you need to understand per code-line.

Sorry if that didn't make any sense.

Reply to this Comment

I can understand that (for teaching purposes). When actually writing something to use this, it seems easier to me to just do it in one line and not have to worry about value/reference issues and remembering to assign things back to your xml object in a second line.

One other note (maybe this is more style based as well)...

Instead of:
xmlRoot.Personality.XmlAttributes.Fun = true
I tend to use:
StructInsert(xmlRoot.PersonalityXmlAttributes, "Fun", true);

This essentially does the same thing, but after spending some time troubling to troubleshoot some weird errors (I have a process that creates a custom xml "file" in memory, then uses that xml transformed with xsl to write CF files), I started doing the second instead of the first to be able to control the case of the xml attributes. Not sure why, but no matter how you type the attribute name in the first example, it always ended up with each letter capitalized (which broke my xsl, which was looking for a specific case of the attribute apparently??). The second example allowed me to control exactly how the attribute name was written out.

Hope that helps somebody else.

Reply to this Comment

@Rich,

That is good to know about the case of the tag name when using StructInsert(). I did notice that they were call coming out upper cased in my CFDumps. I figured since CF isn't case sensitive it doesn't matter... but I guess when dealing with XML, more things are case sensitive, including this XSLT that you use.

That is a really good tip to know. Thanks.

Reply to this Comment

Even though I love CF, I have found a really nice Java Library called JDOM to do these things, which, if you put the jar files in your server you can do things in a much nicer (and faster!) way.

check it out!

http://www.jdom.org/

Reply to this Comment

@Mark,

That's funny you mention is because my recent XML exploration was triggered by someone asking me about JDOM. I don't know anything about it, but it made me realize that I haven't done too much with XML either. I hear good stuff about this JDOM. Thanks for the link. I will check it out.

Reply to this Comment

A while ago I had to create an xml document using the described method. That worked very well. The only problem I had was adding a stylesheet to the xml document.
I ended up replacing the first node name, with my stylesheet and adding the first node name again.

Something like:
<cfset xmldoc = replaceNocase(xmldoc, '<rootnode>', '<?xml-stylesheet type="text/css" href="/default.css" ?>
<rootnode>, 'ONE')>

I'm sure there's a better method....... i hope?

Reply to this Comment

@Ron,

I am not sure how you get/set the style sheet node with the API. I am trying to look into it, but not coming up with much.

Reply to this Comment

Have there been any improvements to the treatment of XSL in CF's XML cfscript functions? It seems like such an obvious oversight.

I just finished conversion of a bunch of nasty cfset calls that built up XML to a more elegant XMLnew() scripted solution only to find that now I can't seem to jam the XSL in there before pushing to the browser. Grrr.

Reply to this Comment

@Charles,

I am not exactly sure what you mean? Are you asking me if more XSLT functionality is supported in recent versions of ColdFusion? Or, are you asking me if there is an easier way to build up XML is CFScript?

Reply to this Comment

@Ben,

The primary question should have been "Do newer versions of CF support the inclusion of a XSLT spreadsheet when building XML within ColdFusion cfscript?"

Ron's solution works (thanks Ron) and got me through a weekend of coding, but like many XML features in CF, and as part of your second question...

I am often finding I have to move away from the XML functions to CF functions to do things that really should be native to the XML functions themselves. Like getting the count of child nodes, existence of items in the path, etc. Constantly having to 'backtrack' my brain to reference the XML as array in code is just a little taxing at times. They could make things a little easier to reference. The last part is me just complaining though!

Reply to this Comment

@Charles,

I am sorry, I still don't fully understand your main question. You should be able to access the XmlTransform() method within your CFScript blocks the same way you do in tags. And, XML is just string values, so I can't see any reason why XSL or XML would not be viable options.

As for upgrades to XPath queries, CF8 definitely did add some more support, but I am not sure what exactly. I know you can reference indicies in child notes in the predicates:

/nodeName[ 3 ]/

I think as of CF8, count() is also supported. I did do a bit of exploration a while back:

http://www.bennadel.com/blog/1018-XPath-Support-Expanded-In-ColdFusion-8.htm

... does that help at all?

Reply to this Comment

Hi Ben,
Great article!!

I was wondering if you may have run into this issue:

I am making an xml file by looping over db content, then saving the file to the server.

The problem I am having is the content of xml file is mashed up and not in any readable format to me as well as xml validations.

for example there is a date field in my xml and it gets wrapped to the next line of the xml file and the validation is saying the data is invalid because the data starts on a new line without a opening tag.

I am using cfscript to write out my xml

xo.xmlRoot.XMLChildren[i] = XmlElemNew(xo,"url");
xo.xmlRoot.XMLChildren[i].XMLChildren[1] = XmlElemNew(xo,"loc");

.....

then a cffile to save the content to my xml file.
when viewed raw in notepad its all bunched up and crammed together.

Any help would be great

Thanks
Tim

Reply to this Comment

@Tim,

I typically use the CFXML to build my XML documents these days; I find it much more straight forward, especially when building a documented based on database-driven data. Of course, it only work in tag format and it sounds like you are using CFScript.

Perhaps you need to wrap the text of the given pesky XML node in a CDATA wrapper?

<![CDATA[ ... your text ... ]]>

... that might help the line breaks work better?

Reply to this Comment

Nice little piece of code, but how will it work using a loop from a query.

Now I need to assign some variables to each section, so that I know to which part to add the sub-sections.

I think it is much easier to simple create the tag structure you require without using an ArrayAppend. You know what the tags will be called.

Remember that the container is always called the same (like CD for example) and is defined by its content.

Reply to this Comment

Post A Comment

?
You — Get Out Of My Dreams, Get Into My Comments
Live in the Now
Oops!
Comment Etiquette: Please do not post spam. Please keep the comments on-topic. Please do not post unrelated questions or large chunks of code. And, above all, please be nice to each other - we're trying to have a good conversation here.