Transforming XML to Application Data Using WDDX
by Anne Sandstrom, Allaire Documentation Group
Editor's Note: This article uses CSS properties that may
not be rendered by all browsers. For best results, you should
view this one in IE 4.0+. You can also download
a ZIP archive of this page which contains the article
in HTM and PDF format.
Abstract
This tech note introduces XML, and provides an example of transforming XML data to application data using WDDX.
Contents
What is XML?
XML is a tag-based, user-defined language. Unlike HTML, whose purpose is to format data, XML is used to identify data.
For example, suppose you maintain a Web site that contains information about books. Using HTML, you might enclose the title of each book in the <H1></H1> tag. However, if you wanted to create and then update a table of contents that included all the book titles on your site, you'd have to do a lot of cutting and pasting because HTML doesn't offer a way to programmatically handle entities enclosed in tags other than to display them on the current page.
Enter XML.
Instead of simply displaying your book titles using the same format, XML allows you to identify an entity as a book title. You can then programmatically manipulate all the book titles on your site. And, if another site uses the same book title definition in XML that you do, you can work with them in the same way.
This has far-reaching implications for exchanging and sharing industry-wide standard data, such as banking information.
What Does XML Look Like?
Like HTML, XML is a tag-based language. Unlike HTML, XML consists of user-defined tags to describe elements of data. For example, a book might contain the elements "title," "author," "pagecount," "copyright," and "publisher."
In the example below, we'll look at an invoice as it might appear in XML. The
invoice contains information about the contractor who submitted it. It also
contains information about the particular tasks, hours, and amounts. The information
can be represented as follows:
|
INVOICE
|
|
Invoice ID: 12345
|
Date submitted: 10/24/1999
|
|
Employee ID: 1048
|
|
Name: Marie Smith
|
|
Address: 124 Brown Street, Warren, VT USA 12345
|
|
Phone:
|
802-555-1111
|
|
Fax:
|
802-555-2222
|
|
Email:
|
msmith@myownisp.com
|
|
Date
|
Hours
|
Description
|
Currency
|
Amount
|
|
9/21/99
|
8
|
Create graphics
|
USD
|
45.00
|
|
9/22/99
|
7.5
|
Edit graphics
|
USD
|
45.00
|
|
9/23/99
|
4
|
Edit graphics
|
USD
|
45.00
|
|
9/23/99
|
4
|
Resize graphics
|
USD
|
45.00
|
|
9/24/99
|
8
|
Update graphics
|
USD
|
45.00
|
Here is an example of the invoice as it might appear in XML:
<?xml version="1.0" ?>
<!--invoice from AP Corporation -->
<invoice>
<invoiceID>12345</invoiceID>
<dateSubmitted>1999-10-24T04:10:56-05:00</dateSubmitted>
<contractorInfo>
<employeeID>1048</employeeID>
<firstName>Marie</firstName>
<lastName>Smith</lastName>
<address>
<street>124 Brown Street</street>
<city>Warren</city>
<state>VT</state>
<zip>12345</zip>
<country>USA</country>
</address>
<voice>802-555-1111</voice>
<fax>802-555-2222</fax>
<email>msmith@myownisp.com</email>
</contractorInfo>
<billableHours>
<item>
<date>1999-09-21</date>
<hours>8</hours>
<description>Create graphics</description>
<rate>
<currency>USD</currency>
<amount>45.00</amount>
</rate>
</item>
<item>
<date>1999-09-22</date>
<hours>7.5</hours>
<description>Edit graphics</description>
<rate>
<currency>USD</currency>
<amount>45.00</amount>
</rate>
</item>
<item>
<date>1999-09-23</date>
<hours>4</hours>
<description>Edit graphics</description>
<rate>
<currency>USD</currency>
<amount>45.00</amount>
</rate>
</item>
<item>
<date>1999-09-23</date>
<hours>4</hours>
<description>Resize and compress graphics</description>
<rate>
<currency>USD</currency>
<amount>45.00</amount>
</rate>
</item>
<item>
<date>1999-09-24</date>
<hours>8</hours>
<description>Update graphics</description>
<rate>
<currency>USD</currency>
<amount>45.00</amount>
</rate>
</item>
</billableHours>
</invoice>
Notice that the invoice contains elements such as "billingAddress," which, in turn, contains elements such as, "firstname," "lastname," and "street."
Using XML with ColdFusion
Now, suppose your finance department uses a ColdFusion application to update the database that keeps track of your contracting expenses. The database you want to maintain contains the following fields:
- EmployeeID
- Description
- Hours
- Amount
- Currency
All of your contractors submit their invoices in XML, as in the example above. You need to take the data from the XML invoices and use it to update the contracting expenses database.
Ways to Handle XML in a ColdFusion Application
There are two distinct paths to go from XML to application data: one uses WDDX, while the other relies on the DOM Tree. The following diagram illustrates both methods:
The remainder of this tech note will describe the method that uses WDDX.
Transforming XML Data Using WDDX
Web Dynamic Data Exchange (WDDX) is a format that allows for exchanging and sharing complex data. It is, in fact, XML-based Since ColdFusion applications completely support WDDX, it is a natural choice for dealing with XML data.
Transforming XML data to application data via WDDX is a two step process:
- Transform XML to WDDX using a style sheet (Extensible Stylesheet Language) with XSL Transformations (XSLT).
- Use the CFWDDX tag to handle the transformed data.
Transforming XML to WDDX with XSLT
In order to manipulate the XML data into a more easily usable form, you use eXtensible Stylesheet Language (XSL). More than simply formatting the XML data, XSL produces a result tree, which you can further modify.
Creating an XSL stylesheet
First, you need to create an XSL stylesheet, as follows.
<!--
File: invoice2wddx.xsl
Author: Simeon Simeonov
Version: 0.2
Last modified: December 17, 1999
This stylesheet provides a default translation between the
invoice format and a WDDX packet describing its structure.
In addition, it can be used as a template for mapping other
types of XML into WDDX.
-->
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/
Transform">
<!-- Match invoice root & emit WDDX packet wrapper -->
<xsl:template match="/invoice">
<wddxPacket version="1.0">
<header/>
<data>
<struct>
<xsl:apply-templates/>
</struct>
</data>
</wddxPacket>
</xsl:template>
<!-- Process all elements that map to array variables -->
<xsl:template match="billableHours">
<var name="{name(.)}">
<array length="{count(*)}">
<xsl:apply-templates/>
</array>
</var>
</xsl:template>
<!-- Process all elements that map to struct variables -->
<xsl:template match="invoice|contractorInfo|address|item">
<var name="{name(.)}">
<struct>
<xsl:apply-templates/>
</struct>
</var>
</xsl:template>
<!-- Process all elements that map to string variables -->
<xsl:template
match="firstName|lastName|street|city|state|zip|country|voice|fax|email|
description|currency">
<var name="{name(.)}">
<string>
<xsl:value-of select="text()"/>
</string>
</var>
</xsl:template>
<!-- Process all elements that map to number variables -->
<xsl:template match="invoiceID|employeeID|hours|amount">
<var name="{name(.)}">
<number>
<xsl:value-of select="text()"/>
</number>
</var>
</xsl:template>
<!-- Process all elements that map to date-time variables -->
<xsl:template match="dateSubmitted|date">
<var name="{name(.)}">
<dateTime>
<xsl:value-of select="text()"/>
</dateTime>
</var>
</xsl:template>
</xsl:stylesheet>
Downloading an XML processor
Once you have created your stylesheet, you can then use it in processing the
XML. You need to determine which XSL processor you want to use. This example
uses the XML parser LotusXSL v0.19.1, available at http://www.alphaworks.ibm.com/.
When you download the .jar file, make sure to put it on the class path set in ColdFusion Administrator.
Tip Before you can run the XML parser, make sure you have installed the JDK
1.2 or JRE 1.2, which can be found at http://java.sun.com/products/jdk/
1.2/jre/index.html.
Processing the XML
Once you have installed the XML parser, you can use the CFOBJECT tag to process the XML. <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<html>
<head>
<title>Process XML</title>
</head>
<body>
<!--- Create a Node class to get at its attributes --->
<CFOBJECT TYPE=JAVA
ACTION=CREATE
CLASS="java.io.FileWriter"
NAME=Writer>
<cfset Writer.init("c:\Inetpub\wwwroot\xml_technote\wddx_test.out")>
<CFOBJECT TYPE=JAVA
ACTION=CREATE
CLASS="java.io.PrintWriter"
NAME=PrintWriter>
<cfset printWriter.init(Writer)>
<!--- Create the parser factory, and get the parser --->
<CFOBJECT
TYPE=JAVA
ACTION=CREATE
CLASS="com.lotus.xsl.XSLProcessor"
NAME=xslProcessor>
<cftry>
<CFSET xslProcessor.process
("c:\Inetpub\wwwroot\xml_technote\invoice.xml",
"c:\Inetpub\wwwroot\xml_technote\invoice2wddx.xsl", PrintWriter)>
<cfcatch type="ANY">
<br>
<cfoutput> Exception : #CFCATCH.Detail# </cfoutput>
</cfcatch>
</cftry>
</body>
</html>
Resulting WDDX packet
Once you process the XML using the stylesheet, the result is a WDDX packet, which is a flattened representation of the data. Because the WDDX packet represents the hierarchy in a flattened form, it makes it possible to share the data across the Web and across applications without losing important information about the data's hierarchy.
The WDDX packet that results from processing the XML with the parser looks like this:
<?xml version="1.0" encoding="UTF-8"?>
<wddxPacket version="1.0"><header/><data><struct>
<var name="invoiceID"><number>12345</number></var>
<var name="dateSubmitted"><dateTime>1999-10-24T04:10:56-05:00</
dateTime></var>
<var name="contractorInfo"><struct>
<var name="employeeID"><number>1048</number></var>
<var name="firstName"><string>Marie</string></var>
<var name="lastName"><string>Smith</string></var>
<var name="address"><struct>
<var name="street"><string>124 Brown Street</string></var>
<var name="city"><string>Warren</string></var>
<var name="state"><string>VT</string></var>
<var name="zip"><string>12345</string></var>
<var name="country"><string>USA</string></var>
</struct></var>
<var name="voice"><string>802-555-1111</string></var>
<var name="fax"><string>802-555-2222</string></var>
<var name="email"><string>msmith@myownisp.com</string></var>
</struct></var>
<var name="billableHours"><array length="5">
<var name="item"><struct>
<var name="date"><dateTime>1999-09-21</dateTime></var>
<var name="hours"><number>8</number></var>
<var name="description"><string>Create graphics</string></
var>
<var name="currency"><string>USD</string></var>
<var name="amount"><number>45.00</number></var>
</struct></var>
<var name="item"><struct>
<var name="date"><dateTime>1999-09-22</dateTime></var>
<var name="hours"><number>7.5</number></var>
<var name="description"><string>Edit graphics</string></var>
<var name="currency"><string>USD</string></var>
<var name="amount"><number>45.00</number></var>
</struct></var>
<var name="item"><struct>
<var name="date"><dateTime>1999-09-23</dateTime></var>
<var name="hours"><number>4</number></var>
<var name="description"><string>Edit graphics</string></var>
<var name="currency"><string>USD</string></var>
<var name="amount"><number>45.00</number></var>
</struct></var>
<var name="item"><struct>
<var name="date"><dateTime>1999-09-23</dateTime></var>
<var name="hours"><number>4</number></var>
<var name="description"><string>Resize and compress
graphics</string></var>
<var name="currency"><string>USD</string></var>
<var name="amount"><number>45.00</number></var>
</struct></var>
<var name="item"><struct>
<var name="date"><dateTime>1999-09-24</dateTime></var>
<var name="hours"><number>8</number></var>
<var name="description"><string>Update graphics</string></
var>
<var name="currency"><string>USD</string></var>
<var name="amount"><number>45.00</number></var>
</struct></var>
</array></var>
</struct></data></wddxPacket>
Using CFWDDX to Manipulate the WDDX Packet
Once you have the WDDX packet, as above, you can simply manipulate it as you can any WDDX packet using the CFWDDX tag. For more information on using CFWDDX, see Developing Web Applications and the CFML Language Reference.
|