XSL

Requirements

An XSL file should start with:

<xsl:stylesheet version:"1.0" xmlns:xsl="http://www.w3.org/1999/xsl/Transform">
    ...
</xsl:stylesheet>

XPath

XPath is am XML navigation and selection language.

Path syntax: /step/step | step/step
Step syntax: axis::test[predicate]

XML document used in examples:

1    <?xml version="1.0" encoding="UTF-8" ?>
2    <store>
3        <book>
4            <title lang="en">Catch-22</title>
5            <price>29.95</price>
6        </book>
7        <book>
8            <title lang="en">Walden</title>
9            <price>15.95</price>
10        </book>
11        <book>
12            <title>Nightwatch</title>
13            <price>25.50</price>
14        </book>
15        <store lang="ge">
16            <book>
17                <title lang="ge">Die Verwandlung</title>
18                <price>8.99</price>
19            </book>
20        </store>
21        <overstock>
22            <book>
23                <title lang="en">Ubuntu Kung-Fu</title>
24                <price>34.99</price>
25            </book>
26        </ovestock>
27    </store>
28    <book>
29        <title lang="fr">La Petite Prince</title>
30        <price>18.00</price>
31    </book>

Selectors

nodename = selects all nodes with that name
/ = starts at the root node, absolute path to nodes
// = selects nodes from anywhere that match the selection
. = selects the current node
.. = selects the parent of the current node
@ = selects attributes


XPath: "store"
Selects elements "store" at 2 and 15


XPath: "/store"
Selects element "store" at 2


XPath: "store/book"
Selects elements "book" at 3, 7, 11, and 16


XPath: "//book"
Selects elements "book" at 3, 7, 11, 16, 22, and 28


XPath: "store//book"
Selects elements "book" at 3, 7, 11, 16, and 22


XPath: "//@lang"
Selects elements "title" at 4, 8, 17, 23, 29
Selects element "store" at 15

Predicates

Predicates are conditions added to the end of selectors. They are always inside [].

[1] = a specific index (indexes start at 1)
[last()] = the last element
[last()-1] = the second to last element
[position()<3] = the first and second elements
[@lang] = elements with a lang attribute defined
[@lang='en'] = elements with a lang='en' attribute defined
[price > 35] = element has a direct child called price, and its value is greater than 35
[starts-with(.,'Ubuntu')] = element has inner text that starts with this string
[text()='exact text'] = element has inner text that exactly matches this string

Operators: + - * div = != < <= > >= or and mod


XPath: "/store/book[2]"
Selects element "book" at 7


XPath: "/store/book[last()]"
Selects element "book" at 11


XPath: "//title[@lang]"
Selects elements "title" at 4, 8, 17, 23, and 29


XPath: "store/book[price > 25]/title"
Selects elements "title" at 4 and 12


XPath: "//title[starts-with(.,'Ubuntu')]"
Selects elements "title" at 23

Wildcards

* = any element node
@* = any attribute node
node() = any node of any kind
text() = the text contents of the current node

Multiple Paths

You can union multiple paths with |.


store/book | overstock/book

Axis

Selects a node set relative to the current node.

self = select current node
attribute = selects all attributes of current node
namespace = select all namespace nodes of the current node

ancestor = selects every ancestor
ancestor-or-self = selects every ancestor plus the current node
descendent = select all descendents
descendent-or-self = select all descendents plus the current node

child = select all direct children
parent = select the direct parent

following = select everything in the xml document after the closing tag of the current node
following-sibling = select all siblings of the current node that occur after it
preceding = select everything in the xml document before the current node tag EXCEPT for ancestors of the current node
preceding-sibling = select all siblings of the current node that occurred before it


XPath: "book[3]/ancestor::store"
Selects elements "store" at 2

Testing

You can test an XPath selector in your browser.
- F12 to open developer tools
- go the Inspector (aka Elements) tab
- Ctrl-F to open Search
- test your XPath in the Inspector search bar

XSLT

XSLT stands for Extensible Stylesheet Language Transformation.

XSLT transforms XML documents into XML, TXT, HTMl, etc documents. It can add, remove, rearrange, sort, and change elements and attributes. It can perform tests and make decisions.

An XSLT document can use extension .xslt or .xsl.

The official W3C XSLT namespace is available at "http://www.w3.org/1999/xsl/Transform".

XML document used in examples:

    <?xml version="1.0" encoding="UTF-8" ?>
    <catalog>
        <cd>
            <artist>The Beatles</artist>
            <title>Best of the Beatles</title>
            <price>12.99</price>
        </cd>
    </catalog>

Template

Templates are reusable XSLT blocks.

The match attribute is an XPath defining what elements to apply this template to. All the XSLT within the template is relative to the element the template is applied to.

You can have multiple templates per XSLT file.

Template applied to root node:

<xsl:template match="/">
    ...
</xsl:template>

Named templates:

<xsl:template name="footer">
    ...
</xsl:template>

Apply Templates

Apply-templates finds the appropriate template for each node and applies it.

For each node in current set:

<xsl:apply-templates/>

For each node in current set selected by this XPath:

<xsl:apply-templates select="artist"/>

Example:

<xsl:template match="/">
    <html>
        <body>
            My CDs:
            <xsl:apply-templates/>
        </body>
    </html>
</xsl:template>
<xsl:template match="cd">
    <p>
        <xsl:apply-templates select="title"/> - 
        <xsl:apply-templates select="artist"/>
    </p>
</xsl:template>
<xsl:template match="title">
    Title: <xsl:value-of select="."/>
</xsl:template>
<xsl:template match="artist">
    Artist: <xsl:value-of select="."/>
</xsl:template>

Template Precedence

1. Templates in primary (current) stylesheet are used before imported templates.

2. Templates with a higher "priority" attribute are used first.

3. Templates with any "priority" attribute are used before templates without the attribute.

4. Templates with more specific XPath patterns are used first.

If more than one template is still possible after all that, you'll either get an error or the last template will be selected.

Call Template

You can explicitly used a named template.


<xsl:call-template name="footer"/>

Template Parameters

You can pass parameters into templates, whether you use apply-templates or call-template.


<xsl:apply-templates>
    <xsl:with-param name="paramA" select="artist"/>
    <xsl:with-param name="paramB" select="title"/>
    <xsl:with-param name="paramC">constant</xsl:with-param>
    <xsl:with-param name="paramD" select="'constant'"/>
</xsl:apply-templates>
<xsl:template match="cd">
    <xsl:param name="paramA"/>
    <xsl:param name="paramB"/>
    <xsl:param name="paramC">Default Value</xsl:param>
    ...
    <!-- reference with $paramA -->
</xsl:template>

Variables

XSLT variables are all constants, you cannot change their value.

Place them at the top of the file as global variables, or within a template as local variables.

Declaring variables:

<xsl:variable name="varA">constant</xsl:variable>
<xsl:variable name="varB" select="cd[2]"/>
<xsl:variable name="varC" select="'constant'"/>

Referencing variables:

<xsl:with-param name="paramA" select="$varA"/>    

For-Each

Loops through a node set.

The default sort order is "ascending".
The default sort-data-type is "text".
The options for case-order are "upper-first" and "lower-first".


<xsl:for-each select="catalog/cd">
    <xsl:sort select="artist"/>
    ...
</xsl:for-each>


    <xsl:sort select="price" data-type="number" order="descending"/>


    <xsl:sort select="artist" case-order="upper-first"/>

If

The contents of an If block are only used if the XPath predicate is true.


<xsl:if test="price > 10">
    ...
</xsl:if>

String operations:

test="string-length($paramA) > 0"
test="contains($text, $substring)"
test="substring-before($text, $substring)"
test="substring-after($text, $substring)"

Boolean operations:

test="not($paramB)"

Choose

Choose works like a if,else if,else statement. The first "when" whose XPath predicate returns "true" is used.


<xsl:choose>
    <xsl:when test="price > 10">
        ...
    </xsl:when>
    <xsl:when test="price > 5">
        ...
    </xsl:when>
    <xsl:otherwise>
        ...
    </xsl:otherwise>
</xsl:choose>

Text Node


<xsl:text>plain text</xsl:text>

Attribute Node

Adding an attribute to an element node.


<img>
    <xsl:attribute name="src">
        <xsl:value-of select="image/name"/>
    </xsl:attribute>
</img>

Copy

Outputs a copy of the current node as-is, with no descendents.


<xsl:copy>...</xsl:copy>

Copy Of

Outputs a copy of the selected node as-is, with all descendents.


<xsl:copy-of select="header"/>

Comments


<xsl:comment>Comments</xsl:comment>

Error

Outputs an error message and (optionally) terminates transformation.


<xsl:message terminate="yes">Message</xsl:message>

Output

An XSLT document can only have one "output" element, and it must be directly under the "xsl:stylesheet" or "xsl:transform" element.

It defines the format of the output document. There are a lot of optional attributes.


<xsl:output indent="yes"/>

Example

XML input document:

<?xml version="1.0" encoding="UTF-8"?>
<menu>
    <food>
        <name>Eggs Over Easy</name>
        <price>5.95</price>
        <calories>120</calories>
    </food>
    <food>
        <name>Waffles</name>
        <price>8.95</price>
        <calories>420</calories>
    </food>
    <food>
        <name>Oatmeal</name>
        <price>3.50</price>
        <calories>230</calories>
    </food>
</menu>

XSLT document:

<?xml version="1.0" encoding="UTF-8"?>
<html xsl:version="1.0" xmlns:xsl="http://www.w3.org/1999/xsl/Transform">
    <body>
        <xsl:for-each select="menu/food">
            <div>
                <p>
                    <xsl:value-of select="name"/> - $<xsl:value-of select="price"/>
                </p>
                <span>
                    (<xsl:value-of select="calories"/> calories)
                </span>
            </div>
        </xsl:for-each>
    </body>
</html>

HTML output document:

<html>
    <body>
        <div>
            <p>
                Eggs Over Easy - $5.95
            </p>
            <span>
                (120 calories)
            </span>
        </div>
        <div>
            <p>
                Waffles - $8.95
            </p>
            <span>
                (420 calories)
            </span>
        </div>
        <div>
            <p>
                Oatmeal - $3.50
            </p>
            <span>
                (230 calories)
            </span>
        </div>
    </body>
</html>