STEP By STEP TUTORIALS OF XSLT,A COMPLETE TUTORIALS OF XSLT,AN ADVANCED TUTORIALS OF XSLT,XML
Introduction
Writing the First XSL
XSL Basics
More XSL Syntax
Final Exercise
Introduction
Prerequisites:
This material assumes that the learner does have a good degree of understanding on the following:
• Understanding of HTML/DHTML
• Understanding of XML
XSL - The Style Sheet of XML
Because XML does not use predefined tags (we can use any tags we want), the meanings of these tags are not understood: <table> could mean an HTML table or maybe a piece of furniture. Because of the nature of XML, the browser does not know how to display an XML document.
In order to display XML documents, it is necessary to have a mechanism to describe how the document should be displayed. One of these mechanisms is CSS (cascaded styles sheet), but XSL (the eXtensible Stylesheet Language) is the preferred style sheet language of XML, and XSL is far more sophisticated than the CSS used by HTML.
XSL - More than a Style Sheet
XSL consists of three parts:
• A method for transforming XML documents
• A method for defining XML parts and patterns
• A method for formatting XML documents
Or simply put, we may think of XSL as a language that can transform XML into HTML, a language that can filter and sort XML data, a language that can address parts of an XML document, a language that can format XML data based on the data value, like displaying negative numbers in red, and a language that can output XML data to different devices, like screen, paper or voice. It means, not only HTML, XSL can be used to transform XML to WML, PDF, and other formats, even to another XML.
Separating content from style
Contrary to when style information is hard-coded into the content, separation of style from content allows for the same data to be presented in different ways. There are several advantages of this approach:
• Reuse of fragments of data: the same content should look different in different contexts
• Multiple output formats: different media (paper, online), different sizes (manuals, reports), different classes of output devices (workstations, hand-held devices)
• Styles tailored to the reader's preference (e.g., accessibility): print size, color, simplified layout for audio readers
• Standardized styles: corporate stylesheets can be applied to the content at any time
• Freedom from style issues for content authors: technical writers needn't be concerned with layout issues because the correct style can be applied later
XSL offers to adopt such an approach where XML document can be treated as pure data while the XSL document does the styling/formatting part of it.
Apart from that, XSL is a World Wide Web standard recommended by W3C. The first two parts of the language (XSLT and XPath) became a W3C Recommendation in November 1999. The full XSL Recommendation including XSL formatting became a W3C Recommendation in October 2001
XML to Result Tree
An XSLT "stylesheet" transforms the input (source) document's tree into a structure called a result tree consisting of result objects. The picture below would help to understand the processing sequence towards an output.
Result tree doctypes
The result tree's structure is that of an XML document, and its objects correspond to elements with attributes. The result tree's structure and "tag set" can match that of any XML document or document type. In particular, the result tree could be:
• HTML/XHTML: Result tree is easily written out as an HTML document.
• Other XML doctype: Result tree is easily written out as an XML document in this other doctype (for some further application-specific processing)
• FO result tree: Result tree's structure (and element and attribute names) matches the set of formatting objects and formatting properties defined by the (non-transformation) part of XSL
Serialization of the result tree is not necessary for further processing of the result tree.
The Components of the XSL Language:
The full XSL language logically consists of three component languages, which are described in three W3C (World Wide Web Consortium) Recommendations:
• XPath: XML Path Language - a language for referencing specific parts of an XML document
• XSLT: XSL Transformations - a language for describing how to transform one XML document (represented as a tree) into another
• XSL: Extensible Stylesheet Language - XSLT plus a description of a set of Formatting Objects and Formatting Properties
Xpath:
Xpath is a language for addressing parts of an XML document, designed to be used by XSLT.
This is the result of an effort to provide a common syntax and semantics for functionality shared between XSL Transformations (XSLT). The primary purpose of XPath is to address parts of an XML document. In support of this primary purpose, it also provides basic facilities for manipulation of strings, numbers and booleans. XPath uses a compact, non-XML syntax to facilitate use of XPath within URIs and XML attribute values. XPath operates on the abstract, logical structure of an XML document, rather than its surface syntax. XPath gets its name from its use of a path notation as in URLs for navigating through the hierarchical structure of an XML document.
In addition to its use for addressing, XPath is also designed so that it has a natural subset that can be used for matching (testing whether or not a node matches a pattern). XPath models an XML document as a tree of nodes. There are different types of nodes, including element nodes, attribute nodes and text nodes. XPath defines a way to compute a string-value for each type of node. Some types of nodes also have names. XPath fully supports XML Namespaces. Thus, the name of a node is modeled as a pair consisting of a local part and a possibly null namespace URI; this is called an expanded-name.
Writing the First XSL
A Simple XML
Let’s start with a simple XML, which we will use as the data source for our first XSL. The XML is illustrated below:
<?xml version="1.0" encoding="ISO-8859-1"?>
<para>
<bold>
Hello, World!
</bold>
</para>
We need to write an XSL to produce an HTML code to display the text “Hello, World!” with the specified format, i.e. on bold style and with a paragraph mark. So, our output HTML code would look as described here.
<HTML>
<HEAD>
<TITLE>XSL Generated HTML</TITLE>
</HEAD>
<BODY>
<P>
<B>Hello, World! </B>
</P>
</BODY>
</HTML>
As we move on, we will learn to achieve such an output using several syntactical ways of writing XSL. One of the simple forms is described here.
<?xml:version=”1.0”?>
<xsl:stylesheet xmlns:xsl=http://www.w3.org/1999/XSL/Transform version=”1.0”>
<xsl:output method=”html” indent=”yes” />
<xsl:template match=”para”>
<P>
<xsl:apply-templates />
</P>
</xsl:template>
<xsl:template match=”bold”>
<B><xsl:apply-templates /></B>
</xsl:template>
</xsl:stylesheet>
HelloWorld.xslt
Running it on a browser:
To display the XML date with the specified style, we need to link the respective XSL with the XML. Let’s say the XML data is saved as a file named HelloWorld.xml; and the respective XSL as HelloWorld.xslt. Putting both of the files within a single directory, we have to add the following line in the XML after the first line:
<?xml-stylesheet type="text/xsl" href="HelloWorld.xslt"?>
The modified HelloWorld.xml would look as below:
<?xml version="1.0" encoding="ISO-8859-1"?>
<?xml-stylesheet type="text/xsl" href="HelloWorld.xslt"?>
<para>
<bold>
Hello, World!
</bold>
</para>
HelloWorld.xml
Now, open this XML file on the Internet Explorer.
Exercises:
1. Currently, the display “Hello, World!” comes in bold font. Insert an additional tag <italic> in the XML to change the text font into bold-italic. Add the respective template in XSL and run it on the browser to check the changed font.
2. Remove all templates from the XSL except the first one, and note the differences.
XSL Basics
Moving forward, we will learn some of the basic activities that can be performed using XSL. Before doing that, let’s construct a bigger XML, which will be, used during the next a few sub-topics. The figure below shows the EmpBasic.xml containing basic information of an employee.
<?xml version="1.0" encoding="ISO-8859-1"?>
<EMPLOYEE ID="2312" STATUS=”C”>Mike Nelson
<FIRST_NAME>Michael</FIRST_NAME>
<MIDDLE_INITIAL>J</MIDDLE_INITIAL>
<LAST_NAME>Nelson</LAST_NAME>
<DATE_OF_BIRTH FORMAT="MM/DD/YYYY">03/16/1965</DATE_OF_BIRTH>
<DATE_OF_JOINING FORMAT="MM/DD/YYYY">07/01/1992</DATE_OF_JOINING>
<DATE_OF_RESIGN FORMAT="MM/DD/YYYY"></DATE_OF_RESIGN>
<SEX>Male</SEX>
<DEPARTMENT ID="421">Salary</DEPARTMENT>
<POSITION>Senior Accountant</POSITION>
<TRANSFER_HISTORY>
<TRANSFER ID="1">
<START_DATE FORMAT="MM/DD/YYYY">08/01/1992</START_DATE>
<END_DATE FORMAT="MM/DD/YYYY">05/31/1996</END_DATE>
<DEPARTMENT ID="132">General Accounting</DEPARTMENT>
<POSITION>Junior Accountant</POSITION>
<LEAVE_TAKEN>20</LEAVE_TAKEN>
</TRANSFER>
<TRANSFER ID="2">
<START_DATE FORMAT="MM/DD/YYYY">06/01/1996</START_DATE>
<END_DATE FORMAT="MM/DD/YYYY">09/30/2000</END_DATE>
<DEPARTMENT ID="244">Vendor Payment</DEPARTMENT>
<POSITION>Accountant</POSITION>
<LEAVE_TAKEN>17</LEAVE_TAKEN>
</TRANSFER>
<TRANSFER ID="3">
<START_DATE FORMAT="MM/DD/YYYY">10/01/2000</START_DATE>
<END_DATE FORMAT="MM/DD/YYYY"></END_DATE>
<DEPARTMENT ID="421">Salary</DEPARTMENT>
<POSITION>Senior Accountant</POSITION>
<LEAVE_TAKEN>20</LEAVE_TAKEN>
</TRANSFER>
</TRANSFER_HISTORY>
</EMPLOYEE>
EmpBasic.xml
Accessing the XML Content:
The content of an XML tag element can be accessed using the value-of construct of XSL. Using this construct both tag element value and tag attribute value can be accessed. The code below shows how to access the employee id and the common name.
<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="html" indent="yes" />
<xsl:template match="EMPLOYEE">
<B>Employee Name: </B><xsl:value-of select="text()" />
<BR />
<B>Employee ID: </B><xsl:value-of select="@ID" />
</xsl:template>
</xsl:stylesheet>
The function text() evaluates the value of the tag EMPLOYEE (the current tag within the template) and @ID evaluates the attribute ID for the EMPLOYEE tag. Using the @ sign followed by the attribute name, the value of the attribute for that tag could be evaluated. To generate a collection of all the attributes, the asterisk (*) is used after the @ sign.
Exercise:
1. Write the XSL code to display the status attribute of the employee?
2. Replace @ID with @* in the XSL code shown above, and run it on the browser.
Using Xpath, the other tag values of the XML can be accessed. The following code shows an example of XSL to access some of the tag values within the EMPLOYEE tag.
<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="html" indent="yes" />
<xsl:template match="EMPLOYEE">
<B>Employee Name: </B><xsl:value-of select="FIRST_NAME" /> <xsl:value-of select="MIDDLE_INITIAL" /> <xsl:value-of select="LAST_NAME" />
<BR />
<B>Date of Joining: </B><xsl:value-of select="DATE_OF_JOINING" /> (<xsl:value-of select="DATE_OF_JOINING/@FORMAT" />)
</xsl:template>
</xsl:stylesheet>
Exercise:
1. What value would be displayed for the following XSL code: <xsl:value-of select=”TRANSFER_HISTORY/TRANSFER/DEPARTMENT” />
Selection criteria can be specified in selecting a particular tag value in case of repeating tags. In the XML, we can see the tag TRANSFER is getting repeated thrice. We want to pick up the values under the TRANSFER tag with ID=”2”. The following example shows the way of specifying Xpath in such situation.
<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="html" indent="yes" />
<xsl:template match="EMPLOYEE">
<B>Department Name: </B><xsl:value-of select="TRANSFER_HISTORY/TRANSFER[@ID='2']/DEPARTMENT" />
<BR />
<B>Position: </B><xsl:value-of select="TRANSFER_HISTORY/TRANSFER[@ID='2']/POSITION" />
<BR />
<B>Start Date: </B><xsl:value-of select="TRANSFER_HISTORY/TRANSFER[@ID='2']/START_DATE" /> (<xsl:value-of select="TRANSFER_HISTORY/TRANSFER[@ID='2']/START_DATE/@FORMAT" />)
<BR />
<B>End Date: </B><xsl:value-of select="TRANSFER_HISTORY/TRANSFER[@ID='2']/END_DATE" /> (<xsl:value-of select="TRANSFER_HISTORY/TRANSFER[@ID='2']/END_DATE/@FORMAT" />)
<BR />
</xsl:template>
</xsl:stylesheet>
For multiple occurrences of a given tag, all matching nodes within the current path can be selected in a single collection using the ‘//’. We will see its use while learning more about templates and recursions.
<xsl:stylesheet>
A stylesheet is represented by an <xsl:stylesheet> element in an XML document. All XSLT code should go inside this element. A stylesheet must have the “version” attribute. Apart from that, it may have the following attributes:
id: specifies the stylesheet ID, a unique identifier
extension-element-prefixes: Identifies name space prefixes that are extension element prefixes
exclude-result-prefixes: Lists name space prefixes that should not be put into the result tree.
<xsl:output>
This element allows the stylesheet authors to specify how they wish the result tree to be output. This element has to be a top-level element. It may contain several attributes, out of which the “method” attribute specifies the method to be used for the result tree output. The method value can be “xml”, or “html”, or “text”, or any other qualified name.
XSL Templates:
XSL uses one or more templates to define how to transform XML elements. An XSL processor parses an XML source and tries to find a matching template rule. If it does, instructions inside matching template are evaluated. Processing always starts from the root template.
A template rule is specified with the <xsl:template> element. The match attribute is a pattern that identifies the source node or nodes to which the rule applies, i.e. a match attribute is used to associate the template with an XML element. The match attribute is required unless the xsl:template element has a name attribute.
<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="html" indent="yes" />
<xsl:template match="EMPLOYEE">
<B>Current Position: </B><xsl:value-of select="POSITION" />
</xsl:template>
</xsl:stylesheet>
If we look at the XML, the top node is EMPLOYEE. Since processing always starts with matching the root template, the code inside the template EMPLOYEE gets evaluated. Accordingly, the label in bold, “Current Position” gets displayed. The value-of construct gets processed to select the specified XML element. It picks up the value of the POSITION node under EMPLOYEE, and displays the value.
Note: A template match must be a valid XML tag.
Exercise:
1. Write the XSL code to display the same using template match=”/”.
Applying Templates:
Templates are applied by using <xsl:apply-templates> statement. It processes a specified node element or the children of the source element from which it has been applied. As example, the XSL code below processes all the node elements under EMPLOYEE. As a result, all the node values gets displayed on the browser sequentially.
<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="html" indent="yes" />
<xsl:template match="EMPLOYEE">
<xsl:apply-templates />
</xsl:template>
</xsl:stylesheet>
The ‘select’ attribute can be used to specify a node or a set of nodes. If this is specified, then only those nodes are processed instead of all children. Fig 3.7 shows the XSL code process POSITION using apply-templates.
<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="html" indent="yes" />
<xsl:template match="EMPLOYEE">
<B>Current Position: </B><xsl:apply-templates select="POSITION" />
</xsl:template>
</xsl:stylesheet>
To apply for multiple elements a pipe () separator is used to separate the elements names, e.g. <xsl:apply-templates select=”POSITION SEX” /> will process both POSITION and SEX. Asterisk (*) selects all possibilities.
If a matching template is present in the XSLT, then the template is evaluated to process the selected element (see the following program).
<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="html" indent="yes" />
<xsl:template match="EMPLOYEE">
<xsl:apply-templates select="POSITION" />
</xsl:template>
<xsl:template match="POSITION">
<B>Current Position: </B><xsl:value-of select="." />
</xsl:template>
</xsl:stylesheet>
Exercise:
1. Display the departments in TRANSFER_HISTORY using apply-templates.
2. What will happen if the apply-templates line is removed?
The “//” is very commonly used in location paths. It is used to select all nodes in the document of specified types. The example below shows the XSL code using “//” to display all the departments mentioned in the XML document.
<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="html" indent="yes" />
<xsl:template match="EMPLOYEE">
<xsl:apply-templates select="//DEPARTMENT" />
</xsl:template>
<xsl:template match="DEPARTMENT">
<B>Department Name: </B><xsl:value-of select="." /><BR />
</xsl:template>
</xsl:stylesheet>
When “//” is used in the middle of the location path, it selects all the nodes appearing within the first part of the location path. The following sample shows the use of “//” only to display the departments under TRANSFER_HISTORY.
<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="html" indent="yes" />
<xsl:template match="EMPLOYEE">
<xsl:apply-templates select="TRANSFER_HISTORY//DEPARTMENT" />
</xsl:template>
<xsl:template match="DEPARTMENT">
<B>Department Name: </B><xsl:value-of select="." /><BR />
</xsl:template>
</xsl:stylesheet>
Named Templates:
Templates can be given any qualified names for identification. Those can be invoked by names using the <xsl:call-template> statement. Unlike apply-templates, call-template does not change the current node or current node list.
<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="html" indent="yes" />
<xsl:template match="EMPLOYEE">
<xsl:call-template name="CurrentPosition" />
</xsl:template>
<xsl:template name="CurrentPosition">
<B>Current Position: </B><xsl:value-of select="POSITION" />
</xsl:template>
</xsl:stylesheet>
Exercise:
1. What will happen if the line 9 of code shown above is replaced with <xsl:template name=”CurrentPosition” match=”POSITION”>?
Template Modes:
Mode is an attribute of templates. With modes, the same node element can be processed differently. In the following example, the POSITION is processed twice to display the value in bold and italic.
<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="html" indent="yes" />
<xsl:template match="EMPLOYEE">
<xsl:apply-templates select="DEPARTMENT" mode="BOLD" />
<xsl:apply-templates select="DEPARTMENT" mode="ITALIC" />
</xsl:template>
<xsl:template match="DEPARTMENT" mode="BOLD">
<B>Department Name: <xsl:value-of select="." /></B><BR />
</xsl:template>
<xsl:template match="DEPARTMENT" mode="ITALIC">
<I>Department Name: <xsl:value-of select="." /></I><BR />
</xsl:template>
</xsl:stylesheet>
Exercise:
1. Use the ‘mode’ attribute to display the top DEPARTMENT element in blue color and those under TRANSFER_HISTORY in green color.
Template Priority:
In case of more than one template qualifying to process the same source element, specifying the priority attribute can prioritize templates. If the priorities are specified, the template with highest priority will be evaluated. The example below shows the display of all DEPARTMENT values with the same bold font.
<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="html" indent="yes" />
<xsl:template match="EMPLOYEE">
<xsl:apply-templates select="//DEPARTMENT" />
</xsl:template>
<xsl:template match="TRANSFER/DEPARTMENT" priority="1">
<I>Department Name: <xsl:value-of select="." /></I><BR />
</xsl:template>
<xsl:template match="DEPARTMENT" priority="2">
<B>Department Name: <xsl:value-of select="." /></B><BR />
</xsl:template>
</xsl:stylesheet>
Exercise:
1. Run the XSL above with priority values reversed.
2. Run the same without any priority values.
More XSL Syntax
Combining Style sheets (Imports/Includes):
Let’s say we need to prepare two different views for the employee name in the XML document EmpBasic.xml. One is in blue, where the name would be shown in blue color, the other one is in red where the name would be shown in red color. The label in both cases would be in bold font. To increase the modularity and reusability of the code, we will put the label in a separate style sheet and combine it with both the edit and view mode style sheets. We will create three XSLT files: EmpBasic.xslt, EmpBasic_Blue.xslt, and EmpBasic_Red.xslt. The XSL codes for the files are shown below:
<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="html" indent="yes" />
<xsl:template match="EMPLOYEE">
<B>Employee Name:</B> <xsl:apply-templates select="FIRST_NAME" /> <xsl:apply-templates select="MIDDLE_INITIAL" /> <xsl:apply-templates select="LAST_NAME" />
</xsl:template>
</xsl:stylesheet>
EmpBasic.xslt
<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:import href="EmpBasic.xslt"/>
<xsl:output method="html" indent="yes" />
<xsl:template match="FIRST_NAME">
<font color="blue"><xsl:value-of select="." /></font>
</xsl:template>
<xsl:template match="MIDDLE_INITIAL">
<font color="blue"><xsl:value-of select="." /></font>
</xsl:template>
<xsl:template match="LAST_NAME">
<font color="blue"><xsl:value-of select="." /></font>
</xsl:template>
</xsl:stylesheet>
EmpBasic_Blue.xslt
<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:import href="EmpBasic.xslt"/>
<xsl:output method="html" indent="yes" />
<xsl:template match="FIRST_NAME">
<font color="red"><xsl:value-of select="." /></font>
</xsl:template>
<xsl:template match="MIDDLE_INITIAL">
<font color="red"><xsl:value-of select="." /></font>
</xsl:template>
<xsl:template match="LAST_NAME">
<font color="red"><xsl:value-of select="." /></font>
</xsl:template>
</xsl:stylesheet>
EmpBasic_Red.xslt
The definitions and template rules in the importing stylesheet take precedence over template rules and definitions in the imported stylesheet. Including a stylesheet is same as importing a stylesheet except that the precedence of the definitions and templates of the included stylesheet remains same as of the including stylesheet.
Exercise:
1. Run the example above using include instead of import.
2. Create two XSLT files: File1.xslt and File2.xslt. Declare the variable “impvar” in File1.xslt with value “One”. Import File1.xslt from File2.xslt.
a. Print the value of “impvar”
b. Declare “impvar” variable in File2.xslt and assign value “Two”. Check which value gets printed.
c. Do the same using xsl:include and check which values are getting printed.
d. Create a new XSLT file File3.xslt. Declare “impvar” with value “Three”. Remove the decalaration from File2.xslt. Include File2.xslt from File3.xslt, and import File1.xslt from File2.xslt. Check which value of “impvar” gets printed.
Variables:
A variable is a name that may be bound to a value. The value to which a variable is bound (the value of the variable) can be an object of any of the types that can be returned by expressions. The following example shows the ways of declaring variables and their usage.
<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="html" indent="yes" />
<xsl:template match="EMPLOYEE">
<xsl:variable name="FullName">
<xsl:value-of select="FIRST_NAME" />
<xsl:value-of select="MIDDLE_INITIAL" />
<xsl:value-of select="LAST_NAME" />
</xsl:variable>
<xsl:variable name="Serial" select="1 + 2 * 3 - 1" />
<xsl:value-of select="$Serial" />. <xsl:value-of select="$FullName" />
</xsl:template>
</xsl:stylesheet>
The XSL block within which the variable is declared limits the scope of a variable. The example below shows that the scopes of the variables are limited within the template only and not accessible from the other template.
<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="html" indent="yes" />
<xsl:template match="EMPLOYEE">
<xsl:variable name="FullName">
<xsl:value-of select="FIRST_NAME" />
<xsl:value-of select="MIDDLE_INITIAL" />
<xsl:value-of select="LAST_NAME" />
</xsl:variable>
<xsl:variable name="Serial" select="1 + 2 * 3 - 1" />
<xsl:call-template name="CheckScope" />
</xsl:template>
<xsl:template name="CheckScope">
<xsl:value-of select="$Serial" />. <xsl:value-of select="$FullName" />
</xsl:template>
</xsl:stylesheet>
Variables can also be defined at root level, i.e. within the <xsl:stylesheet> element. Its scope is throughout the stylesheet.
Note: Variables cannot be reassigned.
Exercise:
1. Declare the same variable in two different stylesheets and combine them using <xsl:include>. Then check that the variable from which stylesheet takes precedence.
2. Combine the stylesheets using <xsl:import> and check the same.
Parameters:
The parameters in XSL is implemented using <xsl:param>. This is similar to the variable, except the value can be bound at the calling template or the stylesheet environment. In the earlier example, we could not access the value of the variables in the second template. Now we will make those values accessible using parameters (see the example below).
<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="html" indent="yes" />
<xsl:template match="EMPLOYEE">
<xsl:variable name="FullName">
<xsl:value-of select="FIRST_NAME" /> <xsl:value-of select="MIDDLE_INITIAL" /> <xsl:value-of select="LAST_NAME" />
</xsl:variable>
<xsl:variable name="Serial" select="1 + 2 * 3 - 1" />
<xsl:call-template name="CheckScope">
<xsl:with-param name="FullName" select="$FullName" />
<xsl:with-param name="Serial" select="$Serial" />
</xsl:call-template>
</xsl:template>
<xsl:template name="CheckScope">
<xsl:param name="FullName" />
<xsl:param name="Serial" />
<xsl:value-of select="$Serial" />. <xsl:value-of select="$FullName" />
</xsl:template>
</xsl:stylesheet>
Conditional Blocks:
There are two instructions in XSLT that support conditional processing in a template: xsl:if, and xsl:choose. The xsl:if instruction provides simple if-then conditionality, the xsl:choose instruction supports selection of one choice when there are several possibilities.
<xsl:if>
The xsl:if element has a “test” attribute, which specifies an expression. The content is a template. The expression is evaluated and the resulting object is converted to a boolean as if by a call to a boolean function. If the result is true, the content template is instantiated otherwise nothing is created. The example below shows a use of xsl:if which displays only the POSITION for TRANSFER ID=2.
<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="html" indent="yes" />
<xsl:template match="EMPLOYEE">
<xsl:apply-templates select="TRANSFER_HISTORY//POSITION" />
</xsl:template>
<xsl:template match="POSITION">
<xsl:if test="../@ID='2'">
<xsl:value-of select="." />
</xsl:if>
</xsl:template>
</xsl:stylesheet>
<xsl:choose … when … otherwise…>
The xsl:choose element selects one among a number of possible alternatives. It consists of a sequence of xsl:when elements followed by an optional xsl:otherwise element. Each xsl:when element has a single attribute, test, which specifies an expression. The content of the xsl:when and xsl:otherwise elements is a template. When an xsl:choose element is processed, each of the xsl:when elements is tested in turn, by evaluating the expression and converting the resulting object to a boolean as if by a call to the boolean function. The content of the first, and only the first, xsl:when element whose test is true is instantiated. If no xsl:when is true, the content of the xsl:otherwise element is instantiated. If no xsl:when element is true, and no xsl:otherwise element is present, nothing is created.
The following XSL code displays the POSITION with different colors depending upon the TRANSFER id.
<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="html" indent="yes" />
<xsl:template match="EMPLOYEE">
<xsl:apply-templates select="TRANSFER_HISTORY//POSITION" />
</xsl:template>
<xsl:template match="POSITION">
<xsl:choose>
<xsl:when test="../@ID='1'">
<font color="red"><xsl:value-of select="." /></font><BR />
</xsl:when>
<xsl:when test="../@ID='2'">
<font color="blue"><xsl:value-of select="." /></font><BR />
</xsl:when>
<xsl:otherwise>
<font color="green"><xsl:value-of select="." /></font><BR />
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
Recursion using <xsl:for-each>:
<xsl:for-each> allows to loop through a repetitive set of nodes and process every node. The following example displays all the start dates and end dates of TRANSFER using <xsl:for-each>.
<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="html" indent="yes" />
<xsl:template match="EMPLOYEE">
<xsl:for-each select="TRANSFER_HISTORY/TRANSFER">
<B>Start Date: </B><xsl:value-of select="START_DATE" /> <B>End Date: </B><xsl:value-of select="END_DATE" /><BR />
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
Conditional checks can be incorporated in xsl:for-each to exclude any specific element or set of elements. In the following example, the TRANSFER node with id=3 is excluded.
<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="html" indent="yes" />
<xsl:template match="EMPLOYEE">
<xsl:for-each select="TRANSFER_HISTORY/TRANSFER[@ID!='3']">
<B>Start Date: </B><xsl:value-of select="START_DATE" /> <B>End Date: </B><xsl:value-of select="END_DATE" /><BR />
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
Exercise:
1. Display all the attributes of EMPLOYEE using for-each of @*.
Recursion in calling templates:
Recursion can also be achieved by making a template call with the template itself. The following example shows the recursive template call to display the TRANSFER dates.
<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="html" indent="yes" />
<xsl:template match="EMPLOYEE">
<xsl:apply-templates select="TRANSFER_HISTORY" />
</xsl:template>
<xsl:template match="TRANSFER_HISTORY">
<xsl:call-template name="Transfer">
<xsl:with-param name="TransferCount" select="count(TRANSFER)" />
<xsl:with-param name="CurrentPosition" select="1" />
<xsl:with-param name="ReturnValue" />
</xsl:call-template>
</xsl:template>
<xsl:template name="Transfer">
<xsl:param name="TransferCount" />
<xsl:param name="CurrentPosition" />
<xsl:param name="ReturnValue" />
<xsl:choose>
<xsl:when test="$CurrentPosition<$TransferCount">
<xsl:call-template name="Transfer">
<xsl:with-param name="TransferCount" select="$TransferCount" />
<xsl:with-param name="CurrentPosition" select="$CurrentPosition + 1" />
<xsl:with-param name="ReturnValue">
<xsl:value-of select="$ReturnValue" />
Start Date: <xsl:value-of select="TRANSFER[@ID=$CurrentPosition]/START_DATE" /> End Date: <xsl:value-of select="TRANSFER[@ID=$CurrentPosition]/END_DATE" />
</xsl:with-param>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$ReturnValue" />
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
Note, at the end of the recursion, the template “Transfer” returns a concatenated string value. This value can be assigned to a variable also using the following type of XSL code.
<xsl:variable name=”Dates”>
<xsl:call-template name="Transfer">
<xsl:with-param name="TransferCount" select="count(TRANSFER)" />
<xsl:with-param name="CurrentPosition" select="1" />
<xsl:with-param name="ReturnValue" />
</xsl:call-template>
</xsl:variable>
Sorting:
Sorting is specified by adding xsl:sort elements as children of an xsl:apply-templates or xsl:for-each element. The first xsl:sort child specifies the primary sort key, the second xsl:sort child specifies the secondary sort key and so on. When an xsl:apply-templates or xsl:for-each element has one or more xsl:sort children, then instead of processing the selected nodes in document order, it sorts the nodes according to the specified sort keys and then processes them in sorted order. When used in xsl:for-each, xsl:sort elements must occur first. When a template is instantiated by xsl:apply-templates and xsl:for-each, the current node list list consists of the complete list of nodes being processed in sorted order.
xsl:sort has a select attribute whose value is an expression. For each node to be processed, the expression is evaluated with that node as the current node and with the complete list of nodes being processed in unsorted order as the current node list. The resulting object is converted to a string as if by a call to the string function; this string is used as the sort key for that node. The default value of the select attribute is, which will cause the string-value of the current node to be used as the sort key.
This string serves as a sort key for the node. The following optional attributes on xsl:sort control how the list of sort keys are sorted; the values of all of these attributes are interpreted as attribute value templates.
• order specifies whether the strings should be sorted in ascending or descending order; ascending specifies ascending order; descending specifies descending order; the default is ascending
• lang specifies the language of the sort keys; it has the same range of values as xml:lang; if no lang value is specified, the language should be determined from the system environment
• data-type specifies the data type of the strings; the following values are allowed:
o text specifies that the sort keys should be sorted lexicographically in the culturally correct manner for the language specified by lang
o number specifies that the sort keys should be converted to numbers and then sorted according to the numeric value; the sort key is converted to a number as if by a call to the number function; the lang attribute is ignored. The default value is text.
Note: The XSL Working Group plans that future versions of XSLT will leverage XML Schemas to define further values for this attribute.
• case-order has the value upper-first or lower-first; this applies when data-type="text", and specifies that upper-case letters should sort before lower-case letters or vice-versa respectively. For example, if lang="en", then A a B b are sorted with case-order="upper-first" and a A b B are sorted with case-order="lower-first". The default value is language dependent.
Note: It is possible for two conforming XSLT processors not to sort exactly the same. Some XSLT processors may not support some languages. Furthermore, there may be variations possible in the sorting of any particular language that are not specified by the attributes on xsl:sort, for example, whether Hiragana or Katakana is sorted first in Japanese. Future versions of XSLT may provide additional attributes to provide control over these variations. Implementations may also use implementation-specific namespaced attributes on xsl:sort for this.
The following code sorts the data on POSITION value in the xsl:for-each loop.
<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="html" indent="yes" />
<xsl:template match="EMPLOYEE">
<xsl:for-each select="TRANSFER_HISTORY/TRANSFER">
<xsl:sort select="POSITION" order="ascending" />
<B>Position: </B><xsl:value-of select="POSITION" />
<B>Start Date: </B><xsl:value-of select="START_DATE" />
<B>End Date: </B><xsl:value-of select="END_DATE" /><BR />
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
Exercise:
1. Sort the same using xsl:apply-templates
2. Sort on the TRANSFER id in descending order to display the same.
Constructing Elements:
<xsl:element> generates elements at the time of processing. In the following example we will create an element <B> using this instruction.
<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="html" indent="yes" />
<xsl:template match="EMPLOYEE">
<xsl:for-each select="TRANSFER_HISTORY/TRANSFER">
<xsl:sort select="POSITION" order="ascending" />
<xsl:element name="B">Position: </xsl:element><xsl:value-of select="POSITION" />
<xsl:element name="B">Start Date: </xsl:element><xsl:value-of select="START_DATE" />
<xsl:element name="B">End Date: </xsl:element><xsl:value-of select="END_DATE" /><BR />
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
Constructing Attributes:
<xsl:attribute> creates attribute to an element at the time of processing. This is a very useful instruction, particularly while assigning attribute values. In the following example, the employee name is to be displayed within a text field. The name has been assigned as the value to the attribute “value” of the element <input>.
<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="html" indent="yes" />
<xsl:template match="EMPLOYEE">
<input type="text" name="ename">
<xsl:attribute name="value">
<xsl:value-of select="FIRST_NAME" /> <xsl:value-of select="LAST_NAME" />
</xsl:attribute>
</input>
</xsl:template>
</xsl:stylesheet>
Exercise:
1. Generate the <input> element using <xsl:element>
Copying Nodes:
<xsl:copy> and <xsl:copy-of> constructs are used for nodes copying. Xsl:copy element copies only the current node without children and attributes, while copy-of copies everything.
Numbers:
<xsl:number> inserts formatted numbers into output. The format is given with format attribute. The attribute starts with format identification followed by separator characters.
<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="html" indent="yes" />
<xsl:template match="EMPLOYEE">
<xsl:apply-templates select="//TRANSFER" />
</xsl:template>
<xsl:template match="TRANSFER">
<xsl:variable name="Serial"><xsl:value-of select="@ID" /></xsl:variable>
<xsl:number format="i." value="$Serial" /> <xsl:value-of select="DEPARTMENT" />
<BR />
</xsl:template>
</xsl:stylesheet>
Exercise:
1. Try the same example with the following format attribute values: I, 1.0, A., a.
Functions for numeric computations:
There are a few XSL functions, which can be used to perform numeric computations. Those are described below.
“number()”
This converts a string expression to number. If the expression value is not a number, it returns NaN.
“sum()”
This function sums all the numbers of the selected nodes.
<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="html" indent="yes" />
<xsl:template match="EMPLOYEE">
<xsl:value-of select="sum(//LEAVE_TAKEN)" />
</xsl:template>
</xsl:stylesheet>
“ceiling()”
This function returns the next higher integer value of a floating-point number, e.g. ceiling (1.67) would return 2.
“floor()”
This function returns the immediate lower integer value of a floating-point number, e.g. floor(1.67) would return 1.
“round()”
This function returns the nearest integer value of a floating-point number, e.g. round (1.67) would return 2, and round (1.33) would return 1.
Addition, Subtraction, and Multiplication of two or more numbers are done using the common syntax. Below are the examples for these three operations:
• <xsl:value-of select=”//TRANSFER[@ID=’1’]/LEAVE_TAKEN + //TRANSFER[@ID=’2’]/LEAVE_TAKEN”/>
• <xsl:value-of select=”//TRANSFER[@ID=’1’]/LEAVE_TAKEN - //TRANSFER[@ID=’2’]/LEAVE_TAKEN”/>
• <xsl:value-of select=”//TRANSFER[@ID=’1’]/LEAVE_TAKEN * //TRANSFER[@ID=’2’]/LEAVE_TAKEN”/>
Leave a white space on each side of the minus (-) sign, otherwise it might be treated as part of the variable expression.
Division and Modulus are derived using div and mod operator respectively.
• <xsl:value-of select=”//TRANSFER[@ID=’1’]/LEAVE_TAKEN div //TRANSFER[@ID=’2’]/LEAVE_TAKEN”/>
• <xsl:value-of select=”//TRANSFER[@ID=’1’]/LEAVE_TAKEN mod //TRANSFER[@ID=’2’]/LEAVE_TAKEN”/>
Exercise:
1. Write XSL codes to derive the minimum, maximum, and average of the LEAVE_TAKEN values.
Strings:
By default, all XSL node values are treated as strings. There are some functions to perform string manipulation as described below.
“string()”
This function converts the argument into a string. The function is not very commonly used since the expressions are considered string by default. The example below shows a use of this function to determine whether the expression is a number.
<xsl:if test=”string(number(FIRST_NAME))=’NaN’”>
“concat()”
This function concatenates the arguments. Their can be as much arguments as required.
<xsl:value-of select=”concat(FIRST_NAME, ‘ ’, LAST_NAME)” />
“starts-with()”
This function returns true if the first argument string starts with the second argument string, otherwise the function returns false.
<xsl:value-of select=”starts-with(FIRST_NAME, ‘M’)” />
“contains()”
This function returns true if the first argument string contains the second argument string, otherwise the function returns false.
<xsl:value-of select=”contains(FIRST_NAME, ‘h’)” />
“substring-before()”
This function returns the string segment before the occurrence of the second argument in the first argument, e.g. substring-before (‘Michael’,’h’) would return ‘Mic’.
“substring-after()”
This function returns the string segment after the occurrence of the second argument in the first argument, e.g. substring-after (‘Michael’,’h’) would return ‘ael’.
“substring()”
The substring function returns the substring of the first argument starting at the position specified in the second argument with length specified in the third argument. If the third argument is not specified, it returns the substring starting at the position specified in the second argument and continuing to the end of the string.Counting starts with 1. As example, substring(‘Michael’,3,3) would return ‘cha’, while substring(‘Michael’,3) would return ‘chael’.
“normalize-space()”
This function returns the argument string with white space normalized by stripping leading and trailing whitespace and replacing sequences of whitespace characters by a single space.
“string-length()”
This function returns the number of characters in the string.
Exercise:
1. Write the XSL code to find out the position of the string ‘h’ within the string ‘Michael’.
2. Write the XSL code to find out the position of second occurrence of the string ‘s’ within the string ‘substring’.
“translate()”
The translate function returns the first argument string with occurrences of characters in the second argument string replaced by the character at the corresponding position in the third argument string. For example, translate("bar","abc","ABC") returns the string BAr. If there is a character in the second argument string with no character at a corresponding position in the third argument string (because the second argument string is longer than the third argument string), then occurrences of that character in the first argument string are removed. For example, translate("--aaa--","abc-","ABC") returns "AAA". If a character occurs more than once in the second argument string, then the first occurrence determines the replacement character. If the third argument string is longer than the second argument string, then excess characters are ignored.
Note: The translate function is not a sufficient solution for case conversion in all languages. A future version of XPath may provide additional functions for case conversion.
Exercise:
1. Find out the results of the following:
a. translate (‘goose’,’egos’,’EGOS’)
b. translate (‘goose’,’es’,’d’)
c. translate (‘goose’,’gseo’,’bad’)
d. translate (‘goose’,’gseg’,’bksC’)
Node-set functions:
We will discuss some of the functions, which operate in the nodes. Those are: position(), last(), count(), name(), and node-set().
“position()”
This function returns a number corresponding to the context position. The function can be used within a for-each loop or within a template. The following example shows the use of this function to display the serial number.
<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="html" indent="yes" />
<xsl:template match="EMPLOYEE">
<xsl:apply-templates select="//TRANSFER" />
</xsl:template>
<xsl:template match="TRANSFER">
<xsl:value-of select="position()" />. <xsl:value-of select="DEPARTMENT" /><BR />
</xsl:template>
</xsl:stylesheet>
“last()”
This function returns a number equal to the context size. The following example shows the use of the function to display comma separator.
<?xml version=”1.0”?>
<xsl:stylesheet xmlns:xsl=”http://www.w3.org/1999/XSL/Transform” version=”1.0”>
<xsl:output method=”html” indent=”yes” />
<xsl:template match=”EMPLOYEE”>
<xsl:apply-templates select=”//TRANSFER” />
</xsl:template>
<xsl:template match=”TRANSFER”>
<xsl:value-of select=”DEPARTMENT” />
<xsl:if test=”position()!=last()”>
,
</xsl:if>
</xsl:template>
</xsl:stylesheet>
“count()”
This function returns the count of the nodes in the argument node-set, <xsl:value-of select="count(//DEPARTMENT)" /> would return 4.
“name()”
This function returns the name of the node, e.g. <xsl:value-of select=”name()” />.
“node-set()”
This function converts a fragmented tree into a node-set. After conversion, the individual tree elements can be accessed using Xpath. In the following example, and fragmented tree has been declared as a variable and later converted to a node-set for proper access.
<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0" xmlns:msxsl="urn:schemas-microsoft-com:xslt" exclude-result-prefixes=”msxsl”>
<xsl:variable name="FileFormat.ft">
<KEY ID="1">Paper</KEY>
<KEY ID="2">MS Word</KEY>
<KEY ID="3">Word Perfect</KEY>
<KEY ID="4">Adobe Acrobat</KEY>
<KEY ID="5">Post Script</KEY>
</xsl:variable>
<xsl:variable name="FileFormat" select="msxsl:node-set($FileFormat.ft)" />
<xsl:template match=”/”>
<xsl:value-of select=”$FileFormat/KEY[@ID=’3’]” />
</xsl:template>
</xsl:stylesheet>
Note the msxsl namespace declaration at the beginning of the code.
Boolean Functions:
Two Boolean functions are sometimes very useful: true(), and false(). Check the following example:
<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="html" indent="yes" />
<xsl:template match="EMPLOYEE">
<xsl:if test="true()">
true
</xsl:if>
<xsl:if test="not(false())">
not false
</xsl:if>
</xsl:template>
</xsl:stylesheet>
Text:
<xsl:text> inserts literal text into the result tree, e.g. <xsl:text>This is a text</xsl:text>.
Comments:
<xsl:comment> inserts comments into the result tree during the processing. As example, <xsl:comment>This is a comment</xsl:comment> would be generated as <!—This is a comment during runtime.
Final Exercise
1. Write the XSLT program to process the following XML document (Books.xml) and generate the HTML layout as shown in the next page. Use a master list of formats (covering all formats shown in the XML) as XSL variable.
<?xml version=”1.0” encoding=”ISO-8859-1”?>
<BOOK_LIST>
<BOOK ID=”1” ISBN=”1234567890”>
<TITLE>Introduction to XML</TITLE>
<PRICE CURRENCY=”USD”>37.95</PRICE>
<DISCOUNT UNIT=”PCT”>10</DISCOUNT>
<FORMAT_LIST>
<FORMAT ID=”2”>MS Word</FORMAT>
<FORMAT ID=”3”>Word Perfect</FORMAT>
</FORMAT_LIST>
</BOOK>
<BOOK ID=”2” ISBN=”2345678901”>
<TITLE>Introduction to XSL</TITLE>
<PRICE CURRENCY=”USD”>21.99</PRICE>
<DISCOUNT UNIT=”ABS”>3.19</DISCOUNT>
<FORMAT_LIST>
<FORMAT ID=”1”>Paper</FORMAT>
<FORMAT ID=”2”>MS Word</FORMAT>
<FORMAT ID=”4”>Adobe Acrobat</FORMAT>
</FORMAT_LIST>
</BOOK>
<BOOK ID=”3” ISBN=”3456789021”>
<TITLE>XSL Programming</TITLE>
<PRICE CURRENCY=”USD”>49.95</PRICE>
<DISCOUNT UNIT=”PCT”>15</DISCOUNT>
<FORMAT_LIST>
<FORMAT ID=”4”>Adobe Acrobat</FORMAT>
<FORMAT ID=”2”>MS Word</FORMAT>
<FORMAT ID=”3”>Word Perfect</FORMAT>
<FORMAT ID=”1”>Paper</FORMAT>
</FORMAT_LIST>
</BOOK>
<BOOK ID=”4” ISBN=”5678901243”>
<TITLE>XML Unleashed</TITLE>
<PRICE CURRENCY=”USD”>17.99</PRICE>
<DISCOUNT UNIT=”PCT”>22</DISCOUNT>
<FORMAT_LIST>
<FORMAT ID=”1”>Paper</FORMAT>
</FORMAT_LIST>
</BOOK>
</BOOK_LIST>
Books.xml
HTML Output desired:
List of books and their available formats:
Book Name Post Discount Price Format F1 …. Format Fn
B1 X X
…
Bm X X
2. Do the same exercise without using the master list of formats.
3. Arrange the list of books in ascending order of post-discount price.
Sample code for Final Exercise 1
<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0" xmlns:msxsl="urn:schemas-microsoft-com:xslt">
<xsl:output method="xml" indent="yes" />
<xsl:variable name="FormatMaster.ft">
<KEY ID="1">Paper</KEY>
<KEY ID="2">MS Word</KEY>
<KEY ID="3">Word Perfect</KEY>
<KEY ID="4">Adobe Acrobat</KEY>
</xsl:variable>
<xsl:variable name="FormatMaster" select="msxsl:node-set($FormatMaster.ft)" />
<xsl:template match="BOOK_LIST">
<xsl:variable name="FormatCount"><xsl:value-of select="count($FormatMaster/KEY)" /></xsl:variable>
<xsl:variable name="ColCount"><xsl:value-of select="2 + $FormatCount" /></xsl:variable>
<HTML>
<HEAD></HEAD>
<BODY>
<TABLE border="1" bordercolor="#000000" cellspacing="0" cellpadding="0">
<TR>
<TD style="font-family:Arial;font-size:12pt;font-weight:bold">
<xsl:attribute name="colspan"><xsl:value-of select="$ColCount" /></xsl:attribute>
List of Books and their available formats arranged in order of post discount price.
</TD>
</TR>
<TR>
<TD><xsl:attribute name="colspan"><xsl:value-of select="$ColCount" /></xsl:attribute>
</TD>
</TR>
<TR style="font-family:Arial;font-size:10pt;font-weight:bold">
<TD>Book Name</TD>
<TD>Post Discount Price</TD>
<xsl:for-each select="$FormatMaster/KEY">
<TD><xsl:value-of select="." /></TD>
</xsl:for-each>
</TR>
<xsl:apply-templates select="BOOK" />
</TABLE>
</BODY>
</HTML>
</xsl:template>
<xsl:template match="BOOK">
<TR style="font-family:Arial;font-size:10pt">
<TD>
<xsl:value-of select="TITLE" />
</TD>
<TD>
<xsl:variable name="price" select="PRICE" />
<xsl:variable name="discount" select="DISCOUNT" />
<xsl:if test="DISCOUNT/@UNIT='PCT'">
<xsl:value-of select="$price * (1 - $discount div 100)" />
</xsl:if>
<xsl:if test="DISCOUNT/@UNIT='ABS'">
<xsl:value-of select="$price - $discount" />
</xsl:if>
</TD>
<xsl:variable name="AvailableFormats.ft">
<xsl:for-each select="FORMAT_LIST/FORMAT">
<KEY>
<xsl:attribute name="ID"><xsl:value-of select="@ID" /></xsl:attribute>
<xsl:value-of select="text()" />
</KEY>
</xsl:for-each>
</xsl:variable>
<xsl:variable name="AvailableFormats" select="msxsl:node-set($AvailableFormats.ft)" />
<xsl:for-each select="$FormatMaster/KEY">
<xsl:variable name="FormatID" select="@ID" />
<TD>
<xsl:for-each select="$AvailableFormats/KEY">
<xsl:if test="@ID=$FormatID">
X
</xsl:if>
</xsl:for-each>
</TD>
</xsl:for-each>
</TR>
</xsl:template>
</xsl:stylesheet>
Sample code for Final Exercise 3
<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0" xmlns:msxsl="urn:schemas-microsoft-com:xslt">
<xsl:output method="xml" indent="yes" />
<!—Creating the node-set of all format elements of the XML document; this will be used in the LoopFormat template
<xsl:variable name="Formats.ft">
<xsl:for-each select="//FORMAT">
<xsl:sort select="@ID" order="ascending" />
<KEY>
<xsl:attribute name="ID"><xsl:value-of select="@ID" /></xsl:attribute>
<xsl:value-of select="." />
</KEY>
</xsl:for-each>
</xsl:variable>
<xsl:variable name="Formats" select="msxsl:node-set($Formats.ft)" />
<!—Creating the node-set of unique format elements used in the XML document using the LoopFormat template
<xsl:variable name="FormatList.ft">
<xsl:call-template name="LoopFormat">
<xsl:with-param name="IDSet" />
<xsl:with-param name="NameSet" />
<xsl:with-param name="CurrentPos" select="1" />
</xsl:call-template>
</xsl:variable>
<xsl:variable name="FormatList" select="msxsl:node-set($FormatList.ft)" />
<!—Creating the node-set of books; this will be used facilitate the sort
<xsl:variable name="BookList.ft">
<xsl:copy-of select="//BOOK" />
</xsl:variable>
<xsl:variable name="BookList" select="msxsl:node-set($BookList.ft)" />
<!—Computing the post discount price and making a corresponding node-set; the ID is kept to the same as the ID of BOOK element
<xsl:variable name="PostDiscountPrice.ft">
<xsl:for-each select="//BOOK">
<PD_PRICE>
<xsl:attribute name="ID"><xsl:value-of select="@ID" /></xsl:attribute>
<xsl:attribute name="VALUE">
<xsl:variable name="Discount"><xsl:value-of select="DISCOUNT" /></xsl:variable>
<xsl:variable name="Price"><xsl:value-of select="PRICE" /></xsl:variable>
<xsl:if test="DISCOUNT/@UNIT='PCT'">
<xsl:value-of select="$Price * (1 - ($Discount div 100))" />
</xsl:if>
<xsl:if test="DISCOUNT/@UNIT='ABS'">
<xsl:value-of select="$Price - $Discount" />
</xsl:if>
</xsl:attribute>
</PD_PRICE>
</xsl:for-each>
</xsl:variable>
<xsl:variable name="PostDiscountPrice" select="msxsl:node-set($PostDiscountPrice.ft)" />
<!—Coding for display
<xsl:template match="BOOK_LIST">
<xsl:variable name="FormatCount"><xsl:value-of select="count($FormatList/KEY)" /></xsl:variable>
<xsl:variable name="ColCount"><xsl:value-of select="2 + $FormatCount" /></xsl:variable>
<HTML>
<HEAD></HEAD>
<BODY>
<TABLE border="1" bordercolor="#000000" cellspacing="0" cellpadding="0">
<TR>
<TD style="font-family:Arial;font-size:12pt;font-weight:bold">
<xsl:attribute name="colspan"><xsl:value-of select="$ColCount" /></xsl:attribute>
List of Books and their available formats arranged in order of post discount price.
</TD>
</TR>
<TR>
<TD><xsl:attribute name="colspan"><xsl:value-of select="$ColCount" /></xsl:attribute>
</TD>
</TR>
<TR style="font-family:Arial;font-size:10pt;font-weight:bold">
<TD>Book Name</TD>
<TD>Post Discount Price</TD>
<xsl:for-each select="$FormatList/KEY">
<TD><xsl:value-of select="." /></TD>
</xsl:for-each>
</TR>
<xsl:for-each select="$PostDiscountPrice/PD_PRICE">
<xsl:sort order="ascending" select="@VALUE" />
<xsl:variable name="BookID"><xsl:value-of select="@ID" /></xsl:variable>
<xsl:variable name="AvailableFormats" select="$BookList/BOOK[@ID=$BookID]/FORMAT_LIST" />
<TR style="font-family:Arial;font-size:10pt">
<TD><xsl:value-of select="$BookList/BOOK[@ID=$BookID]/TITLE" /></TD>
<TD>
<xsl:value-of select="substring-before($PostDiscountPrice/PD_PRICE[@ID=$BookID]/@VALUE,'.')" />
<xsl:text>.</xsl:text>
<xsl:value-of select="substring (substring-after($PostDiscountPrice/PD_PRICE[@ID=$BookID]/@VALUE,'.'),0,3)" />
</TD>
<xsl:for-each select="$FormatList/KEY">
<xsl:variable name="KeyID"><xsl:value-of select="@ID" /></xsl:variable>
<TD>
<xsl:for-each select="$AvailableFormats/FORMAT">
<xsl:sort select="@ID" order="ascending" />
<xsl:choose>
<xsl:when test="@ID=$KeyID">
X
</xsl:when>
<xsl:otherwise>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each>
</TD>
</xsl:for-each>
</TR>
</xsl:for-each>
</TABLE>
</BODY>
</HTML>
</xsl:template>
<xsl:template name="LoopFormat">
<xsl:param name="IDSet" />
<xsl:param name="NameSet" />
<xsl:param name="CurrentPos" />
<xsl:choose>
<xsl:when test="$CurrentPos<count($Formats/KEY)">
<xsl:variable name="CurrentID" select="$Formats/KEY[$CurrentPos]/@ID" />
<xsl:variable name="CurrentName">
<KEY>
<xsl:attribute name="ID"><xsl:value-of select="$Formats/KEY[$CurrentPos]/@ID" /></xsl:attribute>
<xsl:value-of select="$Formats/KEY[$CurrentPos]" />
</KEY>
</xsl:variable>
<xsl:choose>
<xsl:when test="not(contains($IDSet, $CurrentID))">
<xsl:call-template name="LoopFormat">
<xsl:with-param name="IDSet"><xsl:value-of select="concat ($IDSet,',',$CurrentID)" /></xsl:with-param>
<xsl:with-param name="NameSet">
<xsl:copy-of select="$NameSet" /><xsl:copy-of select="$CurrentName" />
</xsl:with-param>
<xsl:with-param name="CurrentPos" select="$CurrentPos + 1" />
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:call-template name="LoopFormat">
<xsl:with-param name="IDSet"><xsl:value-of select="$IDSet" /></xsl:with-param>
<xsl:with-param name="NameSet"><xsl:copy-of select="$NameSet" /></xsl:with-param>
<xsl:with-param name="CurrentPos" select="$CurrentPos + 1" />
</xsl:call-template>
</xsl:otherwise>
</xsl:choose>
</xsl:when>
<xsl:otherwise>
<xsl:copy-of select="$NameSet" />
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
No comments:
Post a Comment