Using XSL to Present XML Documents

[Previous] [Next]

XSL documents are similar to CSS documents in that they both define styles that apply to certain elements, but there are a few differences. CSS defines the typographical style of only XHTML elements, whereas the styles defined in XSL documents apply to entire XML documents. Moreover, XSL might use the styles specified in CSS to produce the output code from XML data. The XSL document must be placed on the same Web server as the file that references it.

As mentioned, most XML data does not contain elements that define how the data should be presented; therefore, you must use XSL documents to transform this XML data into a form that can be presented to the user. In this way, XSL provides a powerful tool to transform XML documents in virtually any way that is necessary.

In this section, we will look at transforming a BizTalk document's body section from XML to XHTML so that it can be displayed in a browser.

We will use the following code, which came from an earlier code sample for BizTalk, to transform XML to XHTML using XSL.

 <?xml version='1.0' ?> <biztalk_1 xmlns="urn:biztalk-org:biztalk:biztalk_1"> <header> <delivery> <message> <messageID>xyzzy:8</messageID> <sent>1999-01-02T19:00:01+02:00</sent> <subject>Purchase Order</subject> </message> <to> <address>http://www.fabrikam.com/recv.asp</address> <state> <referenceID/> <handle/> <process/> </state> </to> <from> <address>mailto:foo@contoso.com</address> <state> <referenceID>123</referenceID> <handle>7</handle> <process>myprocess</process> </state> </from> </delivery> <manifest> <document> <name>PO</name> <description>Purchase Order</description> </document> </manifest> </header> <body> <PO xmlns= "x-schema: http://schemas.biztalk.org/BizTalk/zi0124pf.xml"> <POHeader> <poNumber>12345</poNumber> <custID>100200300</custID> <description>Order for 200 desktop PCs </description> <paymentType>Invoice</paymentType> <shipType>Express2d</shipType> <Contact> <contactName>John Doe</contactName> <contactEmail>jdoe@fabrikam.com</contactEmail> <contactPhone>4250001212</contactPhone> </Contact> <POShipTo> <attn>Fabrikam Receiving</attn> <street>10 Main Street</street> <city>Anytown</city> <stateProvince>WA</stateProvince> <postalCode>98000</postalCode> <country>USA</country> </POShipTo> <POBillTo> <attn>Fabrikam Payables</attn> <street>101 Headquarters Road</street> <city>Anytown</city> <stateProvince>WA</stateProvince> <postalCode>98000</postalCode> <country>USA</country> </POBillTo> </POHeader> <POLines> <count>2</count> <totalAmount>192000.00</totalAmount> <Item> <line>1</line> <partno>pc1010</partno> <qty>200</qty> <uom>EACH</uom> <unitPrice>800.00</unitPrice> <discount>10</discount> <totalAmount>144000.00</totalAmount> </Item> <Item> <line>1</line> <partno>monitor17</partno> <qty>200</qty> <uom>EACH</uom> <unitPrice>300.00</unitPrice> <discount>20</discount> <totalAmount>48000.00</totalAmount> </Item> </POLines> </PO> </body> </biztalk_1> 

Rename this file NorthwindPO.xml. (You can also find this file on the companion CD.)

There is nothing in the NorthwindPO.xml XML document that specifies how it should be presented in a Web browser. With XSL, you can create an XSL template document that will transform every document with our example's structure into XHTML. Let's look at how we could build an XSL document that will transform our example into XHTML.

NOTE
It's important to realize that at this point, Internet Explorer 5 doesn't distinguish a BizTalk document with special presentation. If you change the element biztalk_1 to some other name, such as say DaffyDuck, it will have no effect on how this document is presented in Internet Explorer 5. The only part of this document that is actually being validated against a schema is the PO element and its child elements. The schema associated with the PO element is located in the BizTalk repository. Currently, nothing is validating the BizTalk elements. If you wanted to validate both the BizTalk elements and the PO element and its child elements, you would have to write your own schema for BizTalk as we did for SOAP in Chapter 8. You could then override the BizTalk body element's content using the xsi namespace as we did with SOAP. For this discussion, we will not be validating the BizTalk elements.

XSL Patterns

XML documents represent a tree of nodes. XSL patterns provide a query language for locating nodes in XML documents. After the nodes in the XML document are identified using a pattern, the nodes will be transformed using a template. The XSL patterns we will be using have the same format as the patterns we used with XPath, such as / (child), // (ancestor), .(current node), @ (attribute), and * (wildcard). In addition to the patterns we already mentioned, XSL also has filter operators to manipulate, sort, and filter XML data.

XSL Filter Operators

The filter operators are similar to the SQL Where clause. A filter is evaluated as a Boolean on every node that is within the current context. If the Boolean evaluates to true, the node is included in the returned set; otherwise, it's excluded. Filters are enclosed in brackets. Remember, in our discussion of XPath in Chapter 6, we used the following path to select certain elements:

 /child::customer [attribute::customerID= c1] [position() = 1] 

This path selects the first customer element from those customer elements that are siblings to the root element that has an attribute equal to c1. The first filter, [attribute::customerID=c1], filters out all the nodes that do not have a customerID attribute equal to c1. The second filter, [position()=1], filters out all the nodes except the node at position one. With XPath, we could rewrite the previous example as follows:

 /child::customer [@customerID= c1] [1] 

We will use this abbreviated form in XSL. There are also some expression operators that we can use when working with XSL. The expression operators are listed in the following table:

XSL Expression Operators

OperatorAlternative syntaxDescription
and$and$Logical-and, shortcut is &&
or$or$Logical-or, shortcut is ||
not()$not$Negation
=$eq$Equality
$ieq$Case-insensitive equality
!=$ne$Not equal
$ine$Case-insensitive inequality
<$lt$Less than
$ilt$Case-insensitive less than
<=$le$Less than or equal
$ile$Case-insensitive less than or equal
>$gt$Greater than
$igt$Case-insensitive greater than
>=$ge$Greater than or equal
$ige$Case-insensitive greater than or equal
$all$Set operation; returns TRUE if the condition is true for all items in the collection
$any$Set operation; returns TRUE if the condition is true for any item in the collection
| Set operation; returns the union of two sets of nodes

Using these operators, we can create filter patterns that are based on comparisons. We will do this in an example in the section "XSL Document Elements."

Transforming XML Using XSL

An XSL document is very similar to an ASP or JSP document in that it can contain a mixture of text (usually XHTML) and scripting code. The XSL document can contain HTML code that will be displayed in a browser and programming instructions that can transform information in an XML document. Putting these two together, you can transform any XML document into XHTML.

To transform the BizTalk document above into XHTML, you will need to add a processing instruction declaring the location of the XSL document that will be used to transform this XML document. To accomplish this, add the following processing instruction to the top of the NorthwindPO.xml document after the XML declaration:

 <?xml-stylesheet type="text/xsl" href="NorthwindPO.xsl"?> 

The type attribute declares that the document referenced by the href attribute is a style sheet and should be used to determine the styles for the elements in the HTML document.

You now need to create the NorthwindPO.xsl document. The XSL document will be a well-formed XML document. In its simplest form, the content of the XSL document is shown in the following code:

 <?xml version="1.0"?> <xsl:stylesheet xmlns:xsl="http://www.w3.org/TR/WD-xsl"> <xsl:template match="/"> <xsl:value-of /> </xsl:template> </xsl:stylesheet> 

As you can see, the XSL template in this example is defined using a small set of XML elements. We'll have a detailed discussion of these elements in the next section.

XSL Document Elements

The most commonly used XSL document elements are stylesheet, copy, value-of, template, and apply-templates. All these elements must be preceded by a namespace when used in an XSL document, as was shown in the NorthwindPO.xsl document. The namespace must be used exactly as it was declared in NorthwindPO.xsl for the XSL document to work in Internet Explorer 5.

The stylesheet element

The stylesheet element is the root of the XSL document, and there can be only one instance of it in the document. The stylesheet element contains the template element and the script element. It can have the following attributes: default-space, indent-result, language, and result-ns. The default-space attribute can be set to preserve to keep the white space in the source document. The indent-result attribute specifies whether you want to preserve white space in the output document. If you like, set this attribute to yes. You can place script within an XSL style sheet. If you do this, the language attribute defines the language you are using, such as Microsoft VBScript or Microsoft JScript. The result-ns attribute tells the processor what the output should be. In the case of Internet Explorer 5, all output will be XHTML, so this attribute will be ignored.

The value-of and template elements

The value-of element selects a node and returns the value of the node as text. The syntax for the value-of element is as follows:

 <xsl:value-of select="pattern"> 

The value-of element will find the pattern within the current context. If the select attribute is not included, select will be set equal to "/", which selects the element and its children. This is what we saw in the previous XSL example document. The value-of element may be a child of the following XSL elements: attribute, comment, copy, element, for-each, if, otherwise, pi, template, and when.

The template element defines a template for the output of a particular type of node. It has the following syntax:

 <xsl:template language="language-name" match="Node-context"> 

The language attribute specifies the scripting language used within the template. The Node-context in the match attribute is a pattern that is also used with XPath. The default pattern is node()|/|@*, which includes all the nodes in the document. The template element can be a child of only the stylesheet and apply-templates elements.

Using the template element, you can set the context for the elements contained within the template element. Using patterns, you can set the context to any element, and its child elements, in the XML document. You can also use a template element to create templates that define how an element should be transformed.

The template element in the previous XSL example document defines an element that we want to match to—in this case, the root element (/ returns the root). Once we have the element, the value-of element will return all the elements specified in a pattern as HTML. Since a pattern was not specified, the default will be used. The default will return everything included in the match, which is the root and all its children, as text. If you try to display the NorthwindPO.xml in a browser, the browser will ignore the XSL elements because they are not HTML elements and will output the content of the elements. The output will appear as shown in Figure 12-1.

click to view at full size.

Figure 12-1. The NorthwindPO.xml document transformed into XHTML.

Now we need to tell Internet Explorer 5 how we want the content displayed in the browser by adding XHTML elements and using styles. Let's look more closely at the elements that can be used in an XSL file.

We could rewrite the XSL document example to create a template that defines how elements should look after transformation as follows:

 <?xml version="1.0"?> <xsl:stylesheet xmlns:xsl="http://www.w3.org/TR/WD-xsl"> <xsl:template match="/"> <html> <style type="text/css"> body {font-family:Arial; font-size:12pt; font-weight:normal; color:blue; line-height:150%} </style> <body> <xsl:value-of select="//body" /> </body> </html> </xsl:template> </xsl:stylesheet> 

This document will now look as shown in Figure 12-2.

click to view at full size.

Figure 12-2. The transformed NorthwindPO.xml document with styles.

We have now created an XSL template that will take any XML document that is valid according to the schema located at http://schemas.biztalk.org/BizTalk/zi0124pf.xml and convert it to XHTML according to the styles and transformations defined in the XSL document.

NOTE
Recall that a schema or DTD defines an entire class of documents. An XSL document will be able to transform any document that is included in that class.

In the previous code, you inserted some HTML into the template, defined a style for the body element, and placed the transformed XML into the body element. In addition, you used the select attribute to select only the text within the body element and its child elements. Unfortunately, the document still appears as one large blob of text. To break the text apart, you would need to rewrite the template as follows:

 <?xml version="1.0"?> <xsl:stylesheet xmlns:xsl="http://www.w3.org/TR/WD-xsl"> <xsl:template match="/"> <html> <style TYPE="text/css"> body {font-family:Arial; font-size:12pt; font-weight:normal; color:blue; line-height:55%} </style> <body> <p> <strong>Message ID: </strong> <xsl:value-of select="//messageID" /><br></br> <strong>Sent: </strong> <xsl:value-of select="//sent" /><br></br> <strong>Subject: </strong> <xsl:value-of select="//subject" /><br></br> <strong>To: </strong> <xsl:value-of select="//to/address" /><br></br> <strong>From: </strong> <xsl:value-of select="//from/address" /><br></br> <strong>PO Number: </strong> <xsl:value-of select="//body//poNumber" /> <br></br> <strong>Customer ID: </strong> <xsl:value-of select="//body//custID" /><br></br> <strong>Description: </strong> <xsl:value-of select="//body//description" /> <br></br> <strong>Contact: </strong> <xsl:value-of select="//body//contactName" /> <br></br> <xsl:value-of select="//body//contactEmail" /> <br></br> <xsl:value-of select="//body//contactPhone" /> <br></br> <strong>POShipTo: </strong> <xsl:value-of select="//body//attn" /><br></br> <xsl:value-of select="//body//street" /> <br></br> <xsl:value-of select="//body//city" /> <xsl:value-of select="//body//stateProvince" /> <xsl:value-of select="//body//postalCode" /> <xsl:value-of select="//body//country" /><br></br> <strong>count: </strong> <xsl:value-of select="//body//count" /><br></br> <strong>Total Amount: </strong> <xsl:value-of select="//body//totalAmount" /> <br></br> <strong>Item:<br></br>Line Number: </strong> <xsl:value-of select="//body//line" /><br></br> <strong>Part Quantity: </strong> <xsl:value-of select="//body//qty" /><br></br> <strong>Part Unit Of Measurement: </strong> <xsl:value-of select="//body//uom" /><br></br> <strong>Part Unit Price: </strong> <xsl:value-of select="//body//unitPrice" /> <br></br> <strong>Part Discount: </strong> <xsl:value-of select="//body//discount" /> <br></br> <strong>Part Total Amount: </strong> <xsl:value-of select="//body//totalAmount" /> <br></br> </p> </body> </html> </xsl:template> </xsl:stylesheet> 

This XML document will now appear as shown in Figure 12-3.

click to view at full size.

Figure 12-3. The formatted NorthwindPO.xml document.

As you can see, this document is now approaching a form that can be easily read. There are still a few problems, such as only one Item element being shown. We will solve this problem with the apply-templates element that will be discussed later in this chapter.

The copy element

The copy element copies the current node into the output without any changes. The node's child nodes and attributes will not be copied automatically. Essentially, this element doesn't convert the XML into XHTML and could be useful if you are already using XHTML.

For example, you could use the copy element as follows:

 <xsl:template match="//body"> <xsl:copy /> </xsl:template> 

This code will output all the body as text without any changes. Thus you can use this element to transform identical data.

The for-each and apply-templates elements

The for-each element has the following syntax:

 <xsl:for-each order-by="sort-criteria-list" select="pattern"> 

The for-each element will iterate over a set of nodes determined by the pattern value of the select attribute. The default value of the select attribute is node()—that is, all nodes that are currently in context. The order-by criteria can be a semicolon-separated list. If the first sort order results in two items that are identical, the second sort criterion, if listed, will be used to determine the order. This pattern will continue throughout the number of criteria listed. You can place a + or - sign before the sort criterion to indicate ascending (the default) or descending order. The for-each element can be a child of the following XSL elements: attribute, comment, copy, element, for-each, if, otherwise, pi, template, and when.

The apply-templates element has the following syntax:

 <xsl:apply-templates order-by="sort-criteria-list" select="pattern"> 

The pattern can be the same as the pattern used in XPath, or you can reference a template element that defines a transformation of an element in the XML document. The order-by element allows you to order the results by a pattern. The apply-templates element will tell the processor to search for and apply any templates that match the select attribute. The search will begin within the XSL document. If a template element is defined that matches the value in the select attribute's pattern, this template element will be used first. Using the template elements, you can create your own templates. We can now place all the Item child elements into a table as follows:

 <?xml version="1.0"?> <xsl:stylesheet xmlns:xsl="http://www.w3.org/TR/WD-xsl"> <xsl:template match="/"> <html> <style TYPE="text/css"> body {font-family:Arial; font-size:12pt; font-weight:normal; color:blue; line-height:55%} </style> <body> <p> <strong>Message ID: </strong> <xsl:value-of select="//messageID" /><br></br> <strong>Sent: </strong> <xsl:value-of select="//sent" /><br></br> <strong>Subject: </strong> <xsl:value-of select="//subject" /><br></br> <strong>To: </strong> <xsl:value-of select="//to/address" /><br></br> <strong>From: </strong> <xsl:value-of select="//from/address" /> <br></br> <strong>PO Number: </strong> <xsl:value-of select="//body//poNumber" /> <br></br> <strong>Customer ID: </strong> <xsl:value-of select="//body//custID" /> <br></br> <strong>Description: </strong> <xsl:value-of select="//body//description" /> <br></br> <strong>Contact: </strong> <xsl:value-of select="//body//contactName" /> <br></br> <xsl:value-of select="//body//contactEmail" /> <br></br> <xsl:value-of select="//body//contactPhone" /> <br></br> <strong>POShipTo: </strong> <xsl:value-of select="//body//attn" /><br></br> <xsl:value-of select="//body//street"/> <br></br> <xsl:value-of select="//body//city" /> <xsl:value-of select="//body//stateProvince" /> <xsl:value-of select="//body//postalCode" /> <xsl:value-of select= "//body//country" /> <br></br> <strong>count: </strong> <xsl:value-of select="//body//count" /> <br></br> <strong>Total Amount: </strong> <xsl:value-of select="//body//totalAmount" /> <br></br>  <strong>Item</strong> <table> <td><strong>Line Number: </strong></td> <td><strong>Part Number: </strong></td> <td><strong>Part Quantity: </strong></td> <td><strong>Part Unit Of Measurement: </strong></td> <td><strong>Part Unit Price: </strong></td> <td><strong>Part Discount: </strong></td> <td><strong>Part Total Amount: </strong></td> <!--Above, a template element was used to define the context. This template element used the                backslash as its match attribute. Thus, the                context for the for-each element is the root                document—that is, the for-each element                can iterate through any element in the                document. In this case, it is iterating                through the Item elements--> <xsl:for-each select="//Item"> <tr> <!-- uses default line template--> <xsl:apply-templates select="line" /> <!-- uses default partno template--> <xsl:apply-templates select="partno" /> <!-- uses default qty template--> <xsl:apply-templates select="qty" /> <!-- uses default uom template--> <xsl:apply-templates select="uom" /> <!-- uses default unitPrice template--> <xsl:apply-templates select="unitPrice" /> <!-- uses default discount template--> <xsl:apply-templates select="discount" /> <!-- uses default totalAmount template--> <xsl:apply-templates select="totalAmount"/> </tr> </xsl:for-each> </table> </p> </body> </html> </xsl:template>  <!--Below are the templates for the table cells.--> <!--The line template uses the default text template.--> <xsl:template match="//body//line"> <td><xsl:apply-templates /></td> </xsl:template> <!--The partno template uses the default text template.--> <xsl:template match="partno"> <td><xsl:apply-templates /></td> </xsl:template> <!--The qty template uses the default text template.--> <xsl:template match="qty"> <td><xsl:apply-templates /></td> </xsl:template> <!—The uom template, uses default text template.--> <xsl:template match="//body//uom"> <td><xsl:apply-templates /></td> </xsl:template> <!-- The unitPrice template uses the default text template.--> <xsl:template match="//body//unitPrice"> <td><xsl:apply-templates /></td> </xsl:template> <!--The discount template uses the default text template.--> <xsl:template match="//body//discount"> <td><xsl:apply-templates /></td> </xsl:template> <!--The totalAmount template uses the default text template.--> <xsl:template match="//body//totalAmount"> <td><xsl:apply-templates /></td> </xsl:template> <!--The default text template.--> <xsl:template match="text()"><xsl:value-of /></xsl:template> </xsl:stylesheet> 

We have made several changes to the code to allow the multiple Item elements to be presented in a table. Figure 12-4 shows what the bottom of this XML document will look like in the browser. As you can see, two items are listed at the bottom of the figure.

click to view at full size.

Figure 12-4. Presenting the NorthwindPO.xml document items in a table.

Look at the bottom of the previous code listing; you can see the following line of code:

 <xsl:template match="text()"><xsl:value-of /></xsl:template> 

This line of code defines a template that handles the text nodes. The XSL draft considers this template one of two built-in templates. The other built-in template is:

 <xsl:template match="/|*"><xsl:apply-templates/></xsl:template> 

Internet Explorer 5 does not have these templates built in, but you can add them to your XSL documents. These built-in templates enable you to create style sheets for irregular data by passing text from the source document to the output automatically.

The xsl:template match="text()" template is used quite often in the XSL documents when an apply-templates element does not have a select attribute, such as in the following declaration:

 <td><xsl:apply-templates /></td> 

Let's look at the following template from the previous code listing, which we will call the totalAmount template, and see what it does:

 <xsl:template match="//body//totalAmount"> <td><xsl:apply-templates /></td> </xsl:template> 

The <xsl:template match="//body//totalAmount"> line of code will create a new totalAmount template. Any apply-templates element that has its select attribute set to totalAmount will be instructing the parser to select this totalAmount template. Because match is set to //body//totalAmount, the content of the totalAmount template will be able to use XSL to transform the totalAmount element in the XML document. The value of match also defines the content of this template element. The content of the totalAmount template includes the start HTML td tag for the table, followed by an XSL apply-templates element and the end td tag. The apply-templates element will use the text template to return the current content as text. Since the current content is the totalAmount element and its attributes and content, the apply-templates element will return the text contents of the totalAmount element. Thus, the totalAmount template will return the start td tag, the text content of the totalAmount element, and the end td tag.

Using this combination of apply-templates elements with template elements, you can create XSL documents that can do virtually anything you require. The template element can contain far more than just HTML elements and the XSL apply-templates element; they can also contain other XSL elements such as choose, when, otherwise, if, script, and eval. Let's look at some of these other elements.

The choose, when, and otherwise elements

The choose, when, and otherwise elements work together to create something similar to what is commonly called a select case statement in other programming languages. The choose element has the following syntax:

 <xsl:choose> 

Either a when or otherwise element will follow the choose element that will allow for the choice between different options. The choose element can be a child element of the following elements: attribute, comment, copy, element, for-each, if, otherwise, pi, template, and when.

The when element provides the testing conditions for the choose element. If the discount element was not already defined in the previous example, we could replace <xsl:apply-templates select="discount" /> with this code:

 <td> <xsl:choose> <xsl:when test="unitPrice[. $lt$ 500]"> 10 </xsl:when> <xsl:when test="unitPrice[. $ge$ 500]"> 5 </xsl:when> <xsl:otherwise> 15 </xsl:otherwise> </xsl:choose> </td> 

NOTE
We could not place this code in a template element in our example XSL documents because the template element would have to refer to the discount element, which would define the content as being only the discount element. Since the unitPrice element is not included within the context of a discount element, a template would not work.

Let's look at how to use the when element in a template. In our example XSL document, you can give a different color to an item depending on its unit price by changing:

 <xsl:template match="//body//unitPrice"> <td><xsl:apply-templates /></td> </xsl:template> 

to this code:

 <xsl:template match="//body//unitPrice"> <xsl:choose> <xsl:when test=".[. $lt$ 500.00]"> <td style="color:green"><xsl:apply-templates /></td> </xsl:when> <xsl:otherwise> <td style="color:red"><xsl:apply-templates /></td> </xsl:otherwise> </xsl:choose> </xsl:template> 

In this example, we used the test attribute with the when element. This example will make the unit price green for items priced under 500 and red for all other items.

The if element

You can use the if element to selectively choose output as shown here:

 <xsl:if test=".[. $lt$ 500.00]">do something if true</xsl:if> 

In the above line of code, test is the condition in the source data that will be tested for being either true or false. In this case, we are testing whether the current element is less than 500. The content of the if statement is a template that will be applied if the test is true. If the expression in this content identifies at least one node in the current source document, the content of xsl:if is placed in the output.

The eval element

The eval element allows you to evaluate a script expression. This element can be used only by a style sheet with the xsl namespace, http://www.w3.org/TR/WD-xsl. You cannot use this element in a style sheet that uses the XSLT namespace, http://www.w3.org/1999/XSL/Transform. We'll examine the XSLT namespace in detail later in this chapter. The eval element can be a child element of the following elements: attribute, comment, copy, element, for-each, if, otherwise, pi, template, and when. You can find the code that illustrates how to use this element in the section "Using the XML DOM to Work with XSLT and XSL" later in this chapter.

The XSL elements for creating XML nodes

You can also use XSL elements to create new nodes that will result in an updated XML document. The elements that are used for creating nodes are: attribute, cdata, comment, element, entity-ref, and pi. If we wanted to create an HTML link attribute for the Item element whose itemID is 0001, we could do the following:

 <xsl:template match="//Item[@itemID=0001]/a"> <xsl:attribute name="href"> http://www.northwindtraders.com/item0001.gif </xsl:attribute> </xsl:template> 

The other node elements will work in a similar manner.



Developing XML Solutions
Developing XML Solutions (DV-MPS General)
ISBN: 0735607966
EAN: 2147483647
Year: 2000
Pages: 115
Authors: Jake Sturm

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