Manipulating Content


Adding and Removing Content

Some books talk about adding and removing content as if it were a significant topic. I think this orientation comes from dealing with result documents that look very similar to source documents, except for adding or subtracting a few Elements or Attributes. In our world this is almost never the case. Even if we are fortunate enough to have similar structures for the source and results trees, they will rarely if ever use the same Element and Attribute names . So, the concept of adding and removing content is somewhat foreign.

However, we do need to deal with these concepts at a very gross level. To add content to a result tree that wasn't in the source tree, we simply insert the appropriate XSLT Elements or literal result markup in the content of the appropriate xsl:template. This is exactly what we did with the first example. There are various ways to ensure that content in the source tree doesn't appear in the result tree. In the most common case, the only things that show up in the result tree are the Elements and Attributes for which we specifically ask, usually by using xsl:value-of. If we don't ask for something, by default it doesn't show up.

Splitting Data Content

A somewhat common problem is splitting data from a single Element or Attribute in the source tree into two or more locations in the result tree. Let's again go back to our scenario of converting prospects to customers, but this time we'll assume that the contact management system has only a single field for the full name . (This is again somewhat of a contrived example, but I can't think of a better one right now.)

We want to turn this:

Source (CombinedContent.xml)
 <?xml version="1.0" encoding="UTF-8"?> <CombinedContent>   <FullName>Fred Public</FullName> </CombinedContent> 

into this:

Result (SplitContent.xml)
 <?xml version="1.0" encoding="UTF-8"?> <SplitContent>   <FirstName>Fred</FirstName>   <LastName>Public</LastName> </SplitContent> 

Here's a template that does it.

Stylesheet (SplitContent.xsl)
 <?xml version="1.0" encoding="UTF-8"?> <xsl:stylesheet version="1.0"     xmlns:xsl="http://www.w3.org/1999/XSL/Transform">   <xsl:output method="xml" version="1.0" encoding="UTF-8"        indent="yes"/>   <xsl:template match="/CombinedContent">     <SplitContent>       <FirstName>         <xsl:value-of select="substring-before(FullName,' ')"/>       </FirstName>       <LastName>         <xsl:value-of select="substring-after(FullName,' ')"/>       </LastName>     </SplitContent>   </xsl:template> </xsl:stylesheet> 

The two xsl:value-of Elements in this example use a function for the expression value of the select Attribute. The substring-before and substring-after functions together work very much the same way that the string token functions do in Java or C/C++. We call substring-before to initialize, then succeeding calls to substring-after return succeeding tokens.

NOTE Real Stylesheets Are More Complicated!

Most of the transformations discussed in this and other sections would normally be coded as templates in a larger, more complex stylesheet. However, I handle the source fragments as complete documents and the stylesheets as complete, stand-alone stylesheets so that you may repeat and experiment with the transformations. You can find all the source documents and stylesheets on the book's Web site.

Combining Data Content

Okay, let's go back the other way. Suppose we need to combine the contents of two different Elements into one. Here are two examples.

First and Last Names to Full Name

This stylesheet does the exact reverse operation of the one in the previous example, combining first and last names into a full name string.

Stylesheet (CombineContent.xsl)
 <?xml version="1.0" encoding="UTF-8"?> <xsl:stylesheet version="1.0"     xmlns:xsl="http://www.w3.org/1999/XSL/Transform">   <xsl:output method="xml" version="1.0" encoding="UTF-8"       indent="yes"/>   <xsl:template match="/SplitContent">     <CombinedContent>       <FullName>         <xsl:value-of select="FirstName"/>         <xsl:text> </xsl:text>         <xsl:value-of select="LastName"/>       </FullName>     </CombinedContent>   </xsl:template> </xsl:stylesheet> 

The use of the FullName start and end tags and the xsl:value-of Element should be fairly intuitive by now. The new thing added in this stylesheet is the xsl:text Element. Under normal circumstances whitespace is stripped from the content of XSLT Elements. There are ways to override this behavior. However, to specifically insert one or more space characters into an Element or Attribute content in the result tree we use xsl:text. Anything within xsl:text, including whitespace, is treated as literal text and written to the result tree.

Converting Time and Zone to ISO 8601

Here's a more involved and more real-world example that uses the same techniques shown above. Many legacy applications maintain the local time of an event. If they maintain the time zone at all, it is probably in a separate field. The ISO 8601 time formats represent local time with an offset from coordinated universal time. For example, 3:15 PM in the U.S. Eastern Standard time zone would be 15:15:00-05:00. For our example, suppose we're processing shipping or customs documents that indicate the time of arrival of an ocean vessel. Our application stores the time of this event using a 24- hour clock and a three-character code for the local time zone. Our ship arrived in New York Harbor at 3:15 in the afternoon.

We want to convert this:

Source (TimeAndZoneToISO8601.xml)
 <?xml version="1.0" encoding="UTF-8"?> <ConvertTime>   <ArrivalTime>     <HourOfDay>15:15</HourOfDay>     <TimeZone>EST</TimeZone>   </ArrivalTime> </ConvertTime> 

to this:

Result (TimeAndZoneInISO8601.xml)
 <?xml version="1.0" encoding="UTF-8"?> <ConvertedTime>   <ArrivalTime>15:15:00-05:00</ArrivalTime> </ConvertedTime> 

Here's the stylesheet.

Stylesheet (TimeAndZoneToISO8601.xsl)
 <?xml version="1.0" encoding="UTF-8"?> <xsl:stylesheet version="1.0"     xmlns:xsl="http://www.w3.org/1999/XSL/Transform">   <xsl:output method="xml" version="1.0" encoding="UTF-8"       indent="yes"/>   <xsl:template match="/ConvertTime/ArrivalTime">   <ConvertedTime>     <ArrivalTime>       <xsl:value-of select="HourOfDay"/>       <xsl:text>:00</xsl:text>       <xsl:if test="TimeZone = 'EST'">-05:00</xsl:if>       <xsl:if test="TimeZone = 'PST'">-08:00</xsl:if>     </ArrivalTime>   </ConvertedTime>   </xsl:template> </xsl:stylesheet> 

We use xsl:text to output the seconds string ":00" to the result tree instead of just using the literal value, though we really don't have to. I've used it here since it seems a bit more readable to me than just inserting the literal value. The feature added in this stylesheet is the xsl:if Element. We perform conditional processing to add an eight-hour offset for West Coast ports using Pacific Standard Time and a five-hour offset for our East Coast ports. The test Attribute on xsl:if is an XPath expression that evaluates to a boolean data type. We test the string content of the TimeZone Element against a literal value. String values such as 'EST' in our example must be enclosed in single quotes since the whole Attribute value is in double quotes.

This is an example of using the procedural programming paradigm in a stylesheet that otherwise follows a rule-based paradigm. Note that the conditional support is fairly meager; there is no if/then/else structure. More complicated conditional processing such as the DOCASE are handled using the xsl:choose, xsl:when , and xsl:otherwise Elements.

Changing an Attribute to an Element

Due to the different ways in which people think about data, you are very likely to see the same item of data treated as an Attribute in one XML document and as an Element in someone else's idea of the same business document. In those types of situations you'll need to convert data between Elements and Attributes.

Here is a fairly common example. Most purchase orders list both a quantity and a unit of measure for a product item. Suppose that our order management system has the capability to import a purchase order in XML. Suppose that the designers decided to depict unit of measure and quantity as separate Elements in an overall line item Element. Suppose further that someone sends us a purchase order coded in compliance with some standard, wherein the standards developers considered that unit of measure was an attribute of quantity ordered rather than a stand-alone bit of data. We will need to convert this:

Source (AttributeToElement.xml)
 <?xml version="1.0" encoding="UTF-8"?> <LineItem>   <QuantityOrdered UOM="CA">100</QuantityOrdered> </LineItem> 

into this:

Result (ElementToAttribute.xml)
 <?xml version="1.0" encoding="UTF-8"?> <LineItem>   <UOM>CA</UOM>   <QtyOrdered>100</QtyOrdered> </LineItem> 

The conversion is very easy, as shown below.

Stylesheet (AttributeToElement.xsl)
 <?xml version="1.0" encoding="UTF-8"?> <xsl:stylesheet version="1.0"     xmlns:xsl="http://www.w3.org/1999/XSL/Transform">   <xsl:output method="xml" version="1.0" encoding="UTF-8"       indent="yes"/>   <xsl:template match="/LineItem">     <LineItem>       <UOM>         <xsl:value-of select="QuantityOrdered/@UOM"/>       </UOM>       <QtyOrdered>         <xsl:value-of select="QuantityOrdered"/>       </QtyOrdered>     </LineItem>   </xsl:template> </xsl:stylesheet> 

The at sign (@) preceding UOM in the location step tells the processor that we want the UOM Attribute of the QuantityOrdered Element.

Changing an Element to an Attribute

Now it's time to generate an invoice, and the billing module of our order management system follows the same conventions as the order entry module. Our customer uses the invoice created by the same standards body that developed the schema used for the purchase order. So, we need to change unit of measure from an Element to an Attribute. We use the same source and result documents as in the previous example, but we reverse their roles. Again, the transformation is pretty simple.

Stylesheet (ElementToAttribute.xsl)
 <?xml version="1.0" encoding="UTF-8"?> <xsl:stylesheet version="1.0"     xmlns:xsl="http://www.w3.org/1999/XSL/Transform">   <xsl:output method="xml" version="1.0" encoding="UTF-8"     indent="yes"/>   <xsl:template match="LineItem">     <LineItem>      <QuantityOrdered UOM="{UOM}">        <xsl:value-of select="QtyOrdered"/>      </QuantityOrdered>    </LineItem>   </xsl:template> </xsl:stylesheet> 

Notice the curly braces ({ } ) on UOM within the quotation marks that delimit the UOM Attribute value. This special syntax tells the processor to evaluate the expression within the braces and replace the braces and expression with the resulting string. This is a very easy way to insert source tree or other values into an Attribute value in a result tree; in fact, it is the only easy way to do it. I must confess that in my initial forays into XSLT I did not discover this particular nifty little feature. I inserted values into Attributes using a convoluted mix of predefined entities and xsl:text with disabled output escaping. This is not a trick you want to try at home.

These few techniques should give you what you need for the most common types of transformations of one or a few data values. We now move to problems in dealing with larger groups of data.



Using XML with Legacy Business Applications
Using XML with Legacy Business Applications
ISBN: 0321154940
EAN: 2147483647
Year: 2003
Pages: 181

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