Creating XML Objects


Listing 14.3 shows an XML document that represents three employees in a corporation.

Listing 14.3. SingleCompany.xmlA Basic XML Document
 <company>   <employee ssn="123-45-6789">     <first>Ed</first>     <last>Johnson</last>     <department>Human Resources</department>   </employee>   <employee ssn="541-29-8376">     <first>Maria</first>     <last>Smith</last>     <department>Accounting</department>   </employee>   <employee ssn="568-73-1924">     <first>Eric</first>     <last>Masters</last>     <department>Accounting</department>   </employee> </company> 

In order to use this document effectively from ColdFusion, we must turn this XML document into an XML object, much like a set of nested structures and arrays. One way to do that is by using the <cfxml> tag.

CAUTION

The two terms XML document and XML object are not interchangeable, although some people use them as such. In this book, XML document refers to a string of XML-encoded text, and XML object refers to a complex variable that ColdFusion can manipulate directly.


Using <cfxml>

Listing 14.4 creates an XML object out of an XML document by using <cfxml>, and displays it by using <cfdump>.

Listing 14.4. cfxml.cfmUsing <cfxml> to Create an XML Object
 <cfxml variable="xmlObject" caseSensitive="Yes"> <company>   <employee ssn="123-45-6789">     <first-name>Ed</first-name>     <last-name>Johnson</last-name>     <department>Human Resources</department>   </employee>   <employee ssn="541-29-8376">     <first-name>Maria</first-name>     <last-name>Smith</last-name>     <department>Accounting</department>   </employee>   <employee ssn="568-73-1924">     <first-name>Eric</first-name>     <last-name>Masters</last-name>     <department>Accounting</department>   </employee> </company> </cfxml> <cfdump var="#xmlObject#"> 

<cfxml> returns the same kind of XML object that is returned by XmlParse(), the only difference being that XmlParse() creates an XML object based on an XML document stored in a string variable. You will likely use XmlParse() more often than you use either <cfxml> or XmlNew(), which is described later in the chapter..

TIP

You may have noticed the caseSensitive attribute in the <cfxml> call earlier. By default, ColdFusion is not case-sensitive in its XML handling. However, because other platforms may be case-sensitive, you should make sure your XML documents are case-sensitive as well.


Accessing XML Elements and Attributes

So now that we have an XML object in memory, how do we access its elements and their properties? ColdFusion makes it easy for us by exposing the elements and attributes as if they were arrays and structures. For example, to access the second employee element, I'd use syntax like this:

 xmlObject["company"]["employee"][2] 

Notice the bracket notation, just as if I were accessing a set of nested structures with an array at the end. What if I wanted to access the second employee's first name? I'd extend the syntax like this:

 xmlObject["company"]["employee"][2]["first-name"].xmlText 

Notice the xmlText property at the end of the reference. If I had just referred to the first-name element, that expression would have returned an XML element object reference rather than a string. The xmlText contains the text stored in the element object itself.

What about attributes? Let's say I wanted to access the SSN of the third employee. I'd use similar syntax, like this:

 xmlObject["company"]["employee"][3].xmlAttributes["ssn"] 

Because attributes are always strings, attributes do not require that you put xmlText at the end of the reference.

TIP

When referencing element and attribute names, I always use bracket notation (["company"]) rather than dot notation (.company). I do this for two reasons. First, XML is case-sensitive, and dot notation often has problems with mixed-case documents. Second, you cannot use dot notation with element names that contain hyphens, so for consistency's sake, I use bracket notation everywhere.


The form of XML notation we've been using so far, in which the element names are used like the names of arrays, is known as short form notation:

 xmlObject["company"]["employee"][2] 

This is different from long form notation, which would reference the second employee node as follows:

 xmlObject.xmlRoot.xmlChildren[2] 

Instead of using company, we use xmlRoot (because it is the document's root node), and instead of using employee[2], we use xmlChildren[2] (because it is the second child of xmlRoot). There is no array reference after xmlRoot because, like all XML documents, this one has only one root element. The advantage to using long form notation is that it doesn't matter what the name of each individual node is, because nodes are referenced using the xmlChildren array of the parent element. The disadvantage is that now you have to do more work to find out what node is being referenced.

The two different notation methods exist for an important reason. Long form notation is like looking at driving directions from MapQuest: The instructions are very detailed but they don't give any perspective on where you are at any given point. In contrast, short form notation is like looking at a map: You can easily see where you are at any given time, but it's not always easy to find out how to get there.

You're not limited to using only one method at a time. You can combine both long form and short form, as I'm doing here:

 xmlObject["company"].xmlChildren[2]["first-name"].xmlText 

Instead of directly referencing the employee array, I'm using xmlChildren.

So far, the only element properties we've seen are xmlText, xmlAttributes, and xmlChildren. These properties are present for every node in a document, and they are, for the most part, the only properties you'll use in most of your code.

Table 14.1 shows a listing of the properties of every element within an XML object.

Table 14.1. XMLNode Properties

PROPERTY NAME

TYPE

DESCRIPTION

xmlName

String

The name of the element. If the element has a namespace prefix, xmlName contains the fully qualified name. (See the later section "XML Namespaces" for more information.)

xmlNsPrefix

String

The element's namespace prefix. If the element does not have a namespace, xmlNsPrefix is blank.

xmlNsURI

String

The element's namespace URI. If the element does not have a namespace, xmlNsURI is blank.

xmlText

String

The text between the element's opening and closing tags (not counting text inside of any child elements).

xmlCData

String

Any CDATA content between the element's opening and closing tags (not counting CDATA sections inside of any child elements). See the section on "CDATA Sections Sections" later in this chapter for more details.

xmlComment

String

A single value containing the text of all the comments inside the element (not counting comments inside of any child elements).

xmlAttributes

Structure

A structure containing a key for each of the element's attributes.

xmlChildren

Array of XML elements

An array containing an entry for each of the element's child elements.

xmlParent

XML element

A reference to the element's parent element.

xmlNodes

Array of XML nodes

An array containing an entry for each child node of the element (not including attribute nodes). This is very rarely used.


Using Array and Structure Functions

Table 14.1 shows that xmlChildren is an array, and xmlAttributes is a structure. This means we can use ColdFusion's native structure and array functions to modify XML objects. If we wanted to delete the third employee node from the document, for instance, we'd use ArrayDeleteAt():

 ArrayDeleteAt(xmlObject["company"].xmlChildren, 3) 

Similarly, to find out how many attributes a particular element has, we'd use StructCount():

 StructCount(xmlObject["company"]["employee"][1].xmlAttributes) 

Developing ColdFusion MX Applications, which is part of the ColdFusion MX documentation set, gives a listing of which array and structure functions can be used with XML objects and when.

Using XmlNew()

<cfxml> is a way to quickly create an XML document from a preexisting XML, but there is another wayone that is often more flexible because you can take very granular control over the content of the document. Listing 14.5 shows the XmlNew() and XmlElemNew() functions in action.

Listing 14.5. XmlNew.cfmUsing XmlNew() and XmlElemNew() to Dynamically Create an XML Object
 <cfscript>   xmlObject = XmlNew("Yes");   xmlObject["company"] = XmlElemNew(xmlObject, "company");   employeeNode1 = XmlElemNew(xmlObject, "employee");   employeeNode1.xmlAttributes["ssn"] = "123-45-6789";   employeeNode1["first-name"] = XmlElemNew(xmlObject, "first-name");   employeeNode1["first-name"].xmlText = "Ed";   employeeNode1["last-name"] = XmlElemNew(xmlObject, "last-name");   employeeNode1["last-name"].xmlText = "Johnson";   employeeNode1["department"] = XmlElemNew(xmlObject, "department");   employeeNode1["department"].xmlText = "Human Resources";   employeeNode2 = XmlElemNew(xmlObject, "employee");   employeeNode2.xmlAttributes["ssn"] = "541-29-8376";   employeeNode2["first-name"] = XmlElemNew(xmlObject, "first-name");   employeeNode2["first-name"].xmlText = "Maria";   employeeNode2["last-name"] = XmlElemNew(xmlObject, "last-name");   employeeNode2["last-name"].xmlText = "Smith";   employeeNode2["department"] = XmlElemNew(xmlObject, "department");   employeeNode2["department"].xmlText = "Accounting";   employeeNode3 = XmlElemNew(xmlObject, "employee");   employeeNode3.xmlAttributes["ssn"] = "568-73-1924";   employeeNode3["first-name"] = XmlElemNew(xmlObject, "first-name");   employeeNode3["first-name"].xmlText = "Eric";   employeeNode3["last-name"] = XmlElemNew(xmlObject, "last-name");   employeeNode3["last-name"].xmlText = "Masters";   employeeNode3["department"] = XmlElemNew(xmlObject, "department");   employeeNode3["department"].xmlText = "Accounting";   ArrayAppend(xmlObject["company"].xmlChildren, employeeNode1);   ArrayAppend(xmlObject["company"].xmlChildren, employeeNode2);   ArrayAppend(xmlObject["company"].xmlChildren, employeeNode3); </cfscript> <cfdump var="#xmlObject#"> 

The XML object created by Listing 14.5 is exactly the same as the one created by Listing 14.4.

When dynamically creating XML documents as we've done in this example, we first use XmlNew() to create a new XML object (the "Yes" is the value of the caseSensitive argument to XmlNew(), which makes the XML object case-sensitive):

 xmlObject = XmlNew("Yes"); 

Then we create elements inside this object:

 xmlObject["company"] = XmlElemNew(xmlObject, "company"); employeeNode1 = XmlElemNew(xmlObject, "employee"); employeeNode1["first-name"] = XmlElemNew(xmlObject, "first-name"); 

We then use ArrayAppend() to place the employee nodes into the company's child node array:

 ArrayAppend(xmlObject["company"].xmlChildren, employeeNode1); 

After appending all the employee nodes into the document, we end up with an XML object that looks exactly like Figure 14.3.

This method of assembling nodes and then using array operations to connect all the nodes together is a common one. Its chief benefit is making code more readable and modular. Otherwise, we'd be using code like this to set the first name:

 xmlObject["company"]["employee"][1]["first-name"] = XmlElemNew(xmlObject, "first-name"); xmlObject["company"]["employee"][1]["first-name"].xmlText = "Ed"; 

Creating XML Documents from Database Content

Sometimes, rather than creating an XML object, you will want to retrieve data from the database and create an XML document that you can then save to disk. That's what Listing 14.6 does.

Listing 14.6. XMLFromADatabase.cfmCreating an XML Document from Database Content
 <cfquery name="employeesQuery" datasource="Chapter14_DSN"> SELECT SSN, FirstName, LastName, Department FROM Employee </cfquery> <cfxml variable="xmlObject" casesensitive="yes"> <company>   <cfoutput query="employeesQuery">   <employee ssn="#XmlFormat(SSN)#">     <first-name>#XmlFormat(FirstName)#</first-name>     <last-name>#XmlFormat(LastName)#</last-name>     <department>#XmlFormat(Department)#</department>   </employee>   </cfoutput> </company> </cfxml> <cffile action="WRITE"     file="#ExpandPath('EmployeeXML.xml')#"     output="#ToString(xmlObject)#"> 

NOTE

To set up Chapter14_DSN, see the Readme.txt inside the Chapter14 folder in the downloadable code for this book.


Listing 14.6 queries the database to find employee information, then creates an XML object and writes the XML object to disk. Running Listing 14.6 produces a file named EmployeeXML.xml, as shown in Listing 14.7.

Listing 14.7. EmployeeXML.xmlXML Generated From A Database
 <?xml version="1.0" encoding="UTF-8"?> <company>   <employee ssn="123-45-6789">     <first-name>Ed</first-name>     <last-name>Johnson</last-name>     <department>Human Resources</department>   </employee>   <employee ssn="541-29-8376">     <first-name>Maria</first-name>     <last-name>Smith</last-name>     <department>Accounting</department>   </employee>   <employee ssn="568-73-1924">     <first-name>Eric</first-name>     <last-name>Masters</last-name>     <department>Accounting</department>   </employee> </company> 

Using ToString() converts an XML object back into an XML document, and the XML document can then be written to disk.

Notice the use of XmlFormat(). Anytime you place data into an XML document, you should always use XmlFormat(), which replaces special or high-ASCII characters with their entity reference equivalents (see the section "Entity References" later in this chapter for more information).



Advanced Macromedia ColdFusion MX 7 Application Development
Advanced Macromedia ColdFusion MX 7 Application Development
ISBN: 0321292693
EAN: 2147483647
Year: 2006
Pages: 240
Authors: Ben Forta, et al

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net