XSLT Tutorial I: Basics and first example application
In November 1999, the World Wide Web Consortium (W3C) issued the XSL transformations, or XSLT for short, as a recommendation - a type of W3C standard. The purpose of this language, written in XML, is to allow webmasters and programmers to take XML data with any desired structure and use them to generate something which the server can supply to the user as an HTML page, a PDF document or in XML form.
The processing of XML sources via an XSLT program basically means that the latter uses the source to generate a document tree and converts this to a result tree by executing the instructions specified in the stylesheet.
Also in November 1999, the consortium agreed on a language allowing access to parts of a document which was intended for XSLT as well as for XPointer (part of the linking specifications which are not yet available in their final form). This XML Path Language (XPath) describes, in the form of expressions, which elements and attributes are to be selected for processing.
iX has published a number of articles on XSLT in the past, some of a more general nature and others written in more specific terms (see articles [[#reference 3]] to [[#reference 6]]). In contrast to the preceding articles, this article will focus on how to get started and how to handle XSLT and XPath, and the examples will become more and more complex. This first part deals with the basics and presents two simple examples of conversion.
What XSL transformations do
XML documents and data can be available in almost any structure whatsoever. Everyone can define his or her own document structure and describe it in a so-called DTD (document type definition). Browsers cannot handle this if they are not provided with a version converted to HTML, a PDF document or an XML version including a reference to an XSLT stylesheet (IE 5.x only in the latter case).
We would first like to present a simple example showing the HTML code which XSLT processors generate from an XML source. The example is based on the document shown in Listing 1. It is a letter consisting of the elements brief and, in it, anrede, text and gruss. This text cannot be displayed by an HTML browser because the latter is not familiar with the elements of the document. An XSLT processor is required to convert the elements to HTML via an XLST stylesheet.
Note: As this example uses a German letter you will possibly find element names and the contents slightly irritating. To add to this, Germans habitually make a difference between a formal or familiar relation resulting in "du" or "Sie" yor "you". An English example would not need this differentiation. But in this case we shall stick to the original listing; so the letter element is a "brief" here ...
Listing 2 contains the stylesheet and Listing 3 the resulting HTML text. The call-up of an XLST processor causes the latter to build up a tree structure of the XML document, read in the stylesheet and generate, from the source tree, a result tree (in this case HTML) which corresponds to the specifications made in the stylesheet.
In this case, this means that the 'processor' first reads the file letter.xml and then letter.xsl. It finally generates the result tree in HTML which the user can/must put into a file. The procedure is top-down: the processor processes the stylesheet hierarchically, searching for an appropriate expression to start with.
The lines at the beginning of Listing 2 are not yet of interest here, but it is absolutely necessary for them to be included in the stylesheet in the form shown here. xsl:templates are the basis for the actual processing. Here the first expression which the processor encounters is
>, the processing of which means that a very lean HTML frame (
>) is generated and the children are processed within this frame if templates are available for them. This is the case here, for they are available for anrede as well as for text and gruss.
For text and gruss, the common template provides for a paragraph (p) containing the content of the element. The pipe in the match attribute means 'or', and xsl:apply-templates causes a deeper hierarchical branch to the children.
The template is more complicated for the form of address because the element contains two attributes which describe the sex and the social relationship. For this reason, the template includes two queries. It is not clear why xsl:if in the specification has no else, so that it is necessary to work with xsl:choose which corresponds to the case query in other programming languages (besides xsl:when, it can also include xsl:otherwise).
In plain language, the embedding of xsl:choose and xsl:if means the following: if you are on familiar terms, write a paragraph with the content 'Liebe/r+*Namen+Komma' ('Dear'), depending on the sex. If you are not on familiar terms, write 'Sehr geehrte/r Frau/Herr' ('Dear Mr/Mrs/Ms') and then the name followed by a comma.
The stylesheet frame is obligatory
And now a word on something which has been neglected up to now: the frame of the stylesheet. When XSLT was designed, it was obviously of special importance for the majority (or the most powerful) in W3C that XSLT stylesheets are XML documents. Besides the XML declaration (line 1), Listing 4 contains essential formalities such as the element xsl:stylesheet, which includes the other elements which take care of processing; however, these are missing here. And in place of xsl:stylesheet, an XSLT document can also include xsl:transform, which has the same function.
Essential in both cases are the attributes version and xmlns:xsl which specify a) which XLST version it is we are dealing with and b) what the namespace prefix is to be. In theory, there could be another character string than xsl here, but who wants to rely on the fact that XSLT processors have not included an identification of this kind during compilation? The string xsl is a kind of 'industrial standard' in this regard. The background to namespace allocation is that it allows several types of documents to be used within a stylesheet and, most important of all, that elements can be identified in an unambiguous way. The difference between the two following elements of the same name should be obvious:
>Once upon a time in the Internet
The URL which identifies the namespace is a convention which means nothing other than that it must be that and nothing else; no web address, no link.
The elements which the XSLT specification identifies as top-level elements can be framed by the element xsl:stylesheet - although they are to be found below the one just mentioned. The adjacent table lists those which bear this remark in the section 'Element Syntax Summary' of the specification. Xsl:import is similar to xsl:include but is not regarded as a top-level element because it cannot be situated in any desired place in this hierarchy but must be specified first (even before xsl:include).
Including and overwriting stylesheets
The fact that xsl:include as well as xsl:import are provided for suggests a difference. There really is a difference, because in the case of xsl:import, the importing stylesheet overwrites the included one in the case of conflict, whereas xsl:include generates a copy. If this causes a conflict, the XSLT processor can report an error - or process the last of several templates.
In this context, 'the same templates' means that the XPath expressions in the match attribute of the xsl:template element are identical (see below). So this must be avoided at all costs.
xsl:import and xsl:include allow stylesheets to be built up in a modular way, which makes them much clearer. Stylesheets which differ in a few details only can be achieved by overwriting imported ones for one or two template(s).
The actual processing of the XML document is caused by the xsl:template elements within the frame shown in Listing 4. In the simplest case, an xsl:template ensures that the elements situated below a node (element) in turn wait for processing. (see Listing 2). In the example given above, this means that first the element brief and, branching from it, the elements anrede, text and gruss are processed.
For the 'addressing' of elements, attributes etc., XPath provides so-called 'expressions' within which it is possible to work with predicates (marked by square brackets) for elements as well as with functions. Starting from the root of the element tree (/), expressions can navigate into the document. For example, /dictionary identifies the element situated directly under the root and //entry finds every entry element at any desired position in the document.
XPath: Predicates and functions
It is possible to navigate on different axes in the document (see below). A common example is to ask for the position in the document.
<xsl:template match="//entry[not (position()=last())]"
looks for entries somewhere in the document which are not the last of their type. Expressions can also contain . and .., which identify the present node or its parent. For example, an expression such as ../@id refers to the attribute of the parent element and entry[@id='dtd'] corresponds with a certain entry, that with the ID dtd.
In the further course of this article, which uses more expressions of this kind, we shall refer to a small dictionary the clear structure of which is appropriate for representing various different accesses to the document tree and the generation of result trees of different kinds. Listing 5 shows the simplest version of the structure: the dictionary contains entries which in turn consist of a possible acronym (acro), the term written out in full and at least one explanation (expl) - explanations in German and English are planned.
Example of dictionary application
Templates are necessary for the table of contents and for the details of the individual entries. Expressed in a complicated way, this would mean generating a template for every hierarchical level of the document tree and thus swinging 'from branch to branch', as Listing 6 suggests: the document element dictionary branches to the entry situated 'below it' and so on. However, this is not necessary, for element types can be addressed in different ways (the same ones can also be addressed several times in different ways).
Also, a few important aspects of XSLT programming come to fruition in these stylesheets: first modularisation, i.e. virtually subdivision into several files, each of which includes all that is required for the table of contents and the individual data. There should thus be three stylesheets:
- toc.xsl: table of contents
- entry.xsl: individual entries
- dictionary.xsl: central stylesheet (frame)
All three are complete stylesheets (even if they cannot be used alone because they are modules) and XML documents. To start with the first two: toc.xsl (Listing 7) includes access to the uppermost element of the source tree: match="dictionary". Even before this, the lines
in the element xsl:stylesheet mean a departure from the field of XSLT specification, for they mean that Michael Kay's Saxon software serves as an XSLT processor. The first line identifies the namespace for saxon, and the second means that saxon is the sign for extensions of the XSLT standard.
And for a very good reason, because Saxon, like other processors, has implemented more than the specification requires. For example, Kay has reimplemented the output element by adding the file attribute, which is why the extended saxon:output is to be found in the dictionary templates in place of xsl:output [Note: as of version 6.1 Kay has replaced saxon:output by saxon:document, which will be the XSLT solution in version 1.1 - still to become more than a working draft, though.]. The file attribute allows us to determine within a template what the result is to be.
As the namespace of the HTML document is to be noted down without a prefix, it occurs in the element xsl:stylesheet without one: xmlns="http://www.w3.org/TR/REC-html40".
Within the saxon:output start tag, two variables generate the path of the result file ($dir and $filesep). They were not declared in toc.xsl because this is done in the above-mentioned frame stylesheet for all which it is planned to include (see below). $dir identifies the directory in which the result of conversion lands. Regardless of the operating system, $filesep provides the separator between directories (and files). Variables can be used within XPath expressions; they usually do not have curly brackets, but here they are used for the purposes of delimitation.
Output in file as desired
Apart from the structure of the actual HTML page, a few specially interesting areas of the stylesheet shall be discussed here. E. g., developers should always use the element xsl:text when it is necessary to output character strings, especially spaces, exactly as planned. XSLT processors as well as HTML viewers treat whitespaces in such a way that, of several consecutive characters such as tabulators, spaces etc., only one single whitespace remains.
Another area shows how to handle one of the control structures known to programmers, of which the if query and the case query have been included in the specification (Saxon also includes a while). As xsl:if does not know xsl:else, it is necessary to realise an alternative via xsl:choose and, included in it, xsl:when as well as xsl:otherwise.
Via the select attribute, for-each iterates through a set of entries and first sorts them according to the value of the acro element present there. xsl:sort is a blank element which includes the sorting key as the value of the select attribute. The element is to be found within xsl:apply-templates or, as here, in an xsl:for-each loop.
The stylesheet then processes the template 'addressed' in an expression for the acro element, but in a certain mode. [[bild_url5] Figure 1] shows the result of Listing 2.
Similarly to the SGML style language DSSSL, XSLT allows elements to be processed differently depending on the context via differing modes=allocations of attributes. In this case, the result for acro in the table of contents should be a
> in each case, which in turn includes a link (
>) to the acronym being processed.
Axes: XPath expressions in short form
For the purposes of comparison: within the page for the individual entry, the acronym template (and that for the term) always looks different, as shown in Listing 9. The template for acro and term included there, which is addressed without a mode, only provides the value of the text node. The pipe stands for 'oder'.
The template which fits to acronyms and is provided with the mode attribute includes, in short form, access to an attribute of the parent element id. This can be done in several ways. The following four expressions in a select describe the same thing (the id given);
ancestor::entry/@id parent::node()/attribute::id parent::node()/@id ../@id
In the first three lines, access is made via one of the so-called XPath axes listed in the box of the same name. These make it possible to move backwards and forwards in documents. ancestor accesses the ancestors of the current node and parent the direct ancestor, the second line using another axis (with attribute). The process is simplified by short forms such as the fourth line above, which is synonymous with the second and third. The first has a more general identification but leads to the same result.
Besides the axes, in particular the functions of XPath and XSLT make the programming of web and other pages easier (see XPath functions). The last template in Listing 7 xsl:template match="entry"mode="do-amount" contains two of the functions; its only purpose is to process the entries differently to above and, via the query
and only for the last element entry, to output its position, which corresponds to the total set of entries. This complicated method is necessary because the xsl:number intended for numbering purposes does not work here (the individual elements must not be numbered).
One of the disadvantages of the XSLT specification (which W3C has acknowledged and included in the demands made on Version 1.1. of the specification, see 'on-line resources') is that it is not possible to generate any desired number of result documents from one source document. As described above, some XSLT processors know the file attribute for one's own implementation of output. The value of the attribute can be put together to form variables, as Listing 7 shows right at the beginning.
requires the three variables dir, filesep and thisentry to have been declared. For the first two, this is done by the frame stylesheet dictionary.xsl. For this reason, thisentry, which is only important for the preparation of the individual entry files, is declared at the beginning of entry.xsl (Listing 8). Within the template for entry, it is given a value which, in conjunction with the other two variables, specifies the result path. In this way, as many files as desired can be generated with one template, for every entry found by the parser forms an output file because the saxon:output only comes to fruition within the entry.
Navigating between entries
The xsl:message following the end tag writes to the standard output, thus keeping developers up-to-date with regard to the progress made by Saxon's work.
Those used to HTML should find a large part of the stylesheet rather familiar. What is error-prone about it is the navigation process. As [[bild_url6] Fig. 2] shows, all entries include up to three references: one to the reference file, one to the previous entry and one to the following entry. The above-mentioned axes help in navigation, but it should be noted that there is a small difference between preceding-sibling and following-sibling. The previous adjacent element and the following adjacent element must be addressed differently (see below).
It is necessary to make a query as to whether the current entry is not the first or last because the stylesheet has to output less in this case. The reference to the reference file must always be made. The two XPath expressions
make it clear whether the beginning or the end has been reached; this decides whether the previous/following element is referenced. There is an obvious difference when a reference is made to one of the two:
preceding-sibling::entry/@id preceding-sibling::entry/acro following-sibling::entry/@id following-sibling::entry/acro
If  is missing in the first two lines, the result is a reference to the very first entry - and not to the one immediately preceding the current entry. This is because, without the '[[#reference 1]]', xsl:value-of outputs the first node in the documentation sequence. Navigation can be realised in this form in many different document cases.
Listing 9 includes the parts of the stylesheet which we have already discussed and ensures, via the root template, that the reference file as well as the individual entries end up in the html directory. The templates for the small details of the stylesheet, like how ref and other things must be processed, can only be found here.
Although this is a small application, it is complete in itself. In the next section, the XML source will have a deeper structure. We shall also be dealing with the numbering of elements. As is usual with tutorials, the executable listings will be available in an archive on the iX FTP server for downloading.