Structuring Stylesheets


We've looked at a couple of examples and some basic concepts. I'll now wrap up this portion of the chapter with a few more general issues before looking at some specific techniques you will find useful. One of these general issues relates to the way we structure our stylesheets.

A stylesheet can have more than one xsl:template. In fact, this is probably the norm. In the rule-based programming paradigm, templates can be applied from other templates in a fashion similar to expanding the nonterminal symbols in BNF. In a procedural paradigm, we can break out certain types of processing into a template and call it in much the same way we might call a function in C. We can even pass parameters to an xsl:template, similar to passing arguments to a C function, and get results returned.

Let's look at two simple examples that demonstrate both techniques. We return to our example of converting a prospect to a customer, but we modify it so that we have more than one prospect or customer in a document. The source document from the contact management system appears below.

Source (Prospects.xml)
 <?xml version="1.0" encoding="UTF-8"?> <Prospects>   <Prospect>     <FName>Wilma</FName>     <MI>J.</MI>     <LName>Peterson</LName>     <Company>Consolidated Consolidators</Company>     <Street1>Suite 35</Street1>     <Street2>14 Friendly Lane</Street2>     <City>Amarillo</City>     <State>TX</State>     <Zip>79101</Zip>   </Prospect>   <Prospect>     <FName>Fred</FName>     <MI>Q.</MI>     <LName>Williamson</LName>     <Company>Independent Grocers of Oklahoma</Company>     <Street1>PO Box 98</Street1>     <City>Bartlesville</City>     <State>OK</State>     <Zip>74003</Zip>   </Prospect>   <Prospect>     <FName>John</FName>     <MI>C.</MI>     <LName>Hernandez</LName>     <Company>Super Service Grocers</Company>     <Street1>Suite 900</Street1>     <Street2>35 Lakeshore Drive</Street2>     <City>Chicago</City>     <State>IL</State>     <Zip>60601</Zip>   </Prospect> </Prospects> 

We want the result document to look as follows for our order management system.

Result (Customers.xml)
 <?xml version="1.0" encoding="UTF-8"?> <Customers>   <Customer>     <FirstName>Wilma</FirstName>     <MiddleName>J.</MiddleName>     <LastName>Peterson</LastName>     <Organization>Consolidated Consolidators</Organization>     <StreetLine1>Suite 35</StreetLine1>     <StreetLine2>14 Friendly Lane</StreetLine2>     <CityOrMunicipalUnit>Amarillo</CityOrMunicipalUnit>     <StateOrProvince>TX</StateOrProvince>     <PostalCode>79101</PostalCode>   </Customer>   <Customer>     <FirstName>Fred</FirstName>     <MiddleName>Q.</MiddleName>     <LastName>Williamson</LastName>     <Organization>Independent Grocers of Oklahoma</Organization>     <StreetLine1>PO Box 98</StreetLine1>     <StreetLine2/>     <CityOrMunicipalUnit>Bartlesville</CityOrMunicipalUnit>     <StateOrProvince>OK</StateOrProvince>     <PostalCode>74003</PostalCode>   </Customer>   <Customer>     <FirstName>John</FirstName>     <MiddleName>C.</MiddleName>     <LastName>Hernandez</LastName>     <Organization>Super Service Grocers</Organization>     <StreetLine1>Suite 900</StreetLine1>     <StreetLine2>35 Lakeshore Drive</StreetLine2>     <CityOrMunicipalUnit>Chicago</CityOrMunicipalUnit>     <StateOrProvince>IL</StateOrProvince>     <PostalCode>60601</PostalCode>   </Customer> </Customers> 

We can construct a stylesheet that uses two templates: one for the root Element that sets up the way the overall document should look, and one for each customer. This example uses xsl:apply-templates .

Stylesheet (ProspectsToCustomersApply.xsl)
 <?xml version="1.0" encoding="UTF-8"?> <xsl:stylesheet version="1.0"     xmlns:xsl="http://www.w3.org/1999/XSL/Transform">   <xsl:template match="/Prospects">       <Customers>         <xsl:apply-templates select="Prospect"/>       </Customers>   </xsl:template>   <xsl:template match="Prospect">     <Customer>       <FirstName>         <xsl:value-of select="FName"/>       </FirstName>       <MiddleName>         <xsl:value-of select="MI"/>       </MiddleName>       <LastName>         <xsl:value-of select="LName"/>       </LastName>       <Organization>         <xsl:value-of select="Company"/>       </Organization>       <StreetLine1>         <xsl:value-of select="Street1"/>       </StreetLine1>       <StreetLine2>         <xsl:value-of select="Street2"/>       </StreetLine2>       <CityOrMunicipalUnit>         <xsl:value-of select="City"/>       </CityOrMunicipalUnit>       <StateOrProvince>         <xsl:value-of select="State"/>       </StateOrProvince>       <PostalCode>         <xsl:value-of select="Zip"/>       </PostalCode>     </Customer>   </xsl:template> </xsl:stylesheet> 

The xsl:apply-templates Element in the root Element's xsl:template tells the processor to apply the templates it finds for the children of the current node. We use the select Attribute in this example to specifically process only the Prospect child Elements of the root Prospects Element. However, since there aren't any other children, we could have left off the select Attribute. When the processor starts executing the xsl:apply-templates it sees if there are any other templates in the stylesheet whose patterns match that of the current node, that is, the current Prospect child of the Prospects root Element. The processor finds a match on the second template and applies that template for each of the children.

We can use this type of structure to break up a complex result document into a shell for the main body and fragments for each child Element. This strategy can greatly aid readability and maintainability, just like breaking up a big program into a main program and a number of smaller subroutines.

Note that this example follows the rule-based paradigm. We focus very much on how we want the result to look and don't tell the processor how to produce the result. The xsl:call-template example below looks very similar and produces the same result, but it follows a different paradigm.

Stylesheet (ProspectsToCustomersCall.xsl)
 <?xml version="1.0" encoding="UTF-8"?> <xsl:stylesheet version="1.0"     xmlns:xsl="http://www.w3.org/1999/XSL/Transform">   <xsl:template match="/Prospects">       <Customers>         <xsl:for-each select="Prospect">           <xsl:call-template name="OutputCustomer"/>         </xsl:for-each>       </Customers>   </xsl:template>   <xsl:template name="OutputCustomer">     <Customer>       <FirstName>         <xsl:value-of select="FName"/>       </FirstName>       <MiddleName>         <xsl:value-of select="MI"/>       </MiddleName>       <LastName>         <xsl:value-of select="LName"/>       </LastName>       <Organization>         <xsl:value-of select="Company"/>       </Organization>       <StreetLine1>         <xsl:value-of select="Street1"/>       </StreetLine1>       <StreetLine2>         <xsl:value-of select="Street2"/>       </StreetLine2>       <CityOrMunicipalUnit>         <xsl:value-of select="City"/>       </CityOrMunicipalUnit>       <StateOrProvince>         <xsl:value-of select="State"/>       </StateOrProvince>       <PostalCode>         <xsl:value-of select="Zip"/>       </PostalCode>     </Customer>   </xsl:template> </xsl:stylesheet> 

There are a few key differences between this and the previous stylesheet. In the content of the first xsl:template we have an xsl:for-each Element that iteratively selects the Prospect child Elements of the root Element. For each of these it performs an xsl:call-template of the xsl:template named OutputCustomer. The second xsl:template is identical to the one in the first stylesheet except that the name Attribute replaces the match Attribute. In this example we are explicitly giving the processor instructions about the algorithm it should use to produce the result tree.

While I generally tend to favor the xsl:apply-templates rule-based paradigm over the xsl:for-each and xsl:call-template procedural paradigm, there are times when an xsl:call-template can make a lot of sense in structuring a complex result tree. Take the case where we have a very complicated document like a health care claim. Such documents are usually broken down into major sections. Suppose that the structure of our source document doesn't match very well the structure of the result document. We could lay out the overall look of the result document and build the result tree using the following template.

 <xsl:template match="/Claim">   <HealthCareClaim>     <xsl:call-template name="OutputProvider"/>     <xsl:call-template name="OutputInsured"/>     <xsl:call-template name="OutputPatient"/>     <xsl:call-template name="OutputServicesProvided"/>   </HealthCareClaim> </xsl:template> 

Even with the rule-based approach using xsl:apply-templates, there are times when you need to be cautious. One of these is when you don't have a template that exactly matches a node in the source tree. In cases like this the processor uses default templates to output the string values of the relevant Elements and its children if any are present. Using xsl:apply-templates on an Element with no child Elements or Attributes usually produces the same results as an xsl:value-of. However, if the Element has children you probably won't get the results you want. Business applications generally cough when data from a bunch of different fields are glommed together rather than being segregated into nice, discrete units.

Another case where I advise caution is when two or more templates match an expression for a source node. There are well-defined rules for deciding which of the templates should be used, but the best thing to do is to avoid coding stylesheets in ways that allow this to happen. Care also needs to be taken when the same Element or Attribute names are used in different parts of a source document. If your templates match only on the node name, templates may get applied to source elements that you didn't want processed . This would produce output in the result document in places where you didn't expect it. So, be careful about how you construct the expressions used for match and select Attributes, and be sure to test them with the widest variety of inputs allowed for the source document.

In addition to breaking up a stylesheet into several xsl:templates, we can also break a logical stylesheet into several different files. The xsl:include and xsl:import Elements bring xsl:templates from other stylesheets into the current one. Similar to the way in which schemas are included in other schemas, included xsl:templates are indistinguishable from those defined in the including stylesheet. There are some subtle differences between imported templates and native templates, particularly in the area of deciding which of two or more templates should be applied when there's a match. I've not seen very many applications for xsl:import yet, so I don't consider those subtleties as appropriate topics for a tutorial. However, be aware that they exist, and be sure to research them before you use xsl:import.

Another point needs to be made here. In these examples I often refer to the first, second, or third xsl:template. By convention I put the xsl:template that matches the root or the root Element first. However, except for the rare (and we hope avoidable) cases where more than one template matches a source tree node, the order of xsl:templates doesn't matter. The xsl:template that matches the root Element could be anywhere in the stylesheet and the result tree would look the same.

We have two more bits of housekeeping to cover before looking at some techniques.



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