|
home—lects—hws
D2L—breeze (snow day)
XSLT, eXtensible Style Language Transformations, is a domain-specific language for transforming XML trees: that is, taking in one XML tree and producing another — a function from XML trees to XML trees! The end result can be any XML; in practice the most common result is an XHTML document.
For example, consider the input
<my_children> <child> <name>Logan</name> <gender>M</gender> <age>7</age> </child> <child> <name>Rebecca</name> <gender>F</gender> <age>3</age> </child> <child> <name>Lee</name> <gender>F</gender> <age>2</age> </child> </my_children>An XSLT program might take this and return a new XML tree:
<html> <body> <h3>Some Kids</h3> <ul> <li>Logan, who is 7</li> <li>Rebecca, who is 3</li> <li>Lee, who is 2</li> </ul> </body> </html>
Rather than write such a function in Java or a general-purpose programming language, XSLT is a domain-specific language, specifically invented to make these tree-transformations easy to write. XSLT programs can easily generate new tags/nodes based on the input, re-order existing nodes, or filter out unwanted nodes.
<xsl:for-each select="nodeSpec"> results... </xsl:for-each> |
<?xml-stylesheet type="text/xsl" href="foo.xsl"?> |
Once the browser has constructed the XML data tree, it then transforms that XML tree using the XSLT instructions, yielding a new tree. The transformation rules are called “templates”; they correspond to functions in a programming language — a function which is given an XML node as input, and returns a (new) XML node.
(Note: usually the output happens to be XHTML, but it can in theory be any XML.)
A particular transformation (“template”)
looks at one node of the input tree [including its subtree],
and produces a (sub)tree to include in the overall output.
In the my_children example above,
there might be one “helper” XSLT template which,
given a
In our context, it's the browser which invokes the XSLT processor, and the result is html. (The XSLT is being run client-side.) However, that's not required — XSLT can produce any XML output, and the XSLT processing can be run stand-alone.
<?xml version="1.0"?> <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"> |
</xsl:stylesheet> |
<xsl:template match="/" > <!-- rule for root-element here --> </xsl:template> |
Note:If you do not include a root template in the XSLT stylesheet, a root template built in to XSLT will be used (typically not what you want).
After running the XSLT program, we have newly-built XML tree;
the very last step is to flatten that tree back to a string
and print it as output.
We can use the
In 95% of all cases, our resulting XML tree is HTML, and so we'll include
<xsl:output method="html"/> |
Note that
Example: Putting the above together, we should now be able to understand the previous example (oneWonder.xml and oneWonder.xsl).
The body of the loop is XSLT,
where the implicit-root of any relative XPATHS is
one of the things selected by the
Note how the syntax of
<p> This person is <xsl:choose> <xsl:when test="height > 200"> extremely </xsl:when> <xsl:when test="height != 0"> <xsl:value-of select="height"/> cm </xsl:when> <xsl:otherwise> unknown </xsl:otherwise> </xsl:choose> tall. </p> |
<xsl:if test="boolean expression"> <!-- other XSLT statements statements --> </xsl:if> |
<xsl:if test="name[@language='English']"> It's called <xsl:value-of select='name'>. </xsl:if> |
observation: Placing thexsl:sort tag as the first-child inside the loop's body is definitely odd. It would be more natural to have a stand-alonesort tag which takes the XPATH of the nodes to sort, and returns a “list” of the sorted nodes. Or, the loop's control-flow should all be set via attributes. Alas, they didn't ask me when designing it. (There is actually a vague scoping reason for their design choice: there are XPATH issues since thexsl:for-each is working on the parent node, but the sort'sselect expression is relative to a child. In most langauges this is solved viasort being passed a function, and that function knows it will handle the individual-element type.)
Suppose we want each Wonder to be a link —
that is instead of the plain text “
<a> <xsl:attribute name="href"> #<xsl:value-of select="name" /> <!-- link-target --> </xsl:attribute> <xsl:value-of select="name" /> <!-- link-text --> |
The general syntax is clear from the preceding example:
<xsl:attribute name="att_name"> <!-- Here, specify the value of the new attribute. Of course, you can use any XML: literal, or and XSLT instructions. This body gets spliced into the enclosing tag (!) --> </xsl:attribute> |
(Like
Note: Thexsl:attribute tag must be the first child within the tag you're trying to affect. If we swapped thexsl:value-of andxsl:attribute nodes within thea , nothing renders at all. (There is no deep conceptual reason for this restriction.)
In the above example, note that although the code happens to work, it is not actually valid:
the value of the
An xslt short-cut: curly-braces
In XSLT, curly-brackets are interpreted as an xpath expression.
This provides a quick-and-dirty way to avoid
This shortcut
that works particularly well when generating attributes:
rather than requiring
Note: We will not emphasize this feature, in this course. Even though abstracting-into-a-function is one of the most fundamental aspects of programming, it's a bit unwieldy (and non-intuitive, and therefore not-particularly-common) to do so in XSLT. (Besides, our interest in XSLT is more as a vehicle to introduce the domain-specific languages xpath and DTDs, rather than than the language XSLT itself.)
The root template is the first thing processed in an XSLT style sheet. XSLT allows you to create more templates than just the root template. This allows you to create different sets of processing rules to apply to different parts of the XML document.
One of the main benefits of using templates is the ability to reuse a template for other nodes in your document. In the same way that one can use functions in most programming languages, you would create a template, and simply apply that template whenever necessary. This eliminates the need to repeat the same processing instructions.
In our ongoing example: We have been putting the non-english name in parentheses and italics. Suppose we want to do that throughout, consistently, and make it easy to maintain any changes or tweaks to this decision. We can define a template to process non-English name nodes, and then apply it in different contexts. (E.g. if there are tags for “newspaper”, we could apply the same template for non-English names there.)
<!-- Define the function --> <xsl:template match="name[@language!='English']"> (<em><xsl:value-of select="."/></em>) </xsl:template> |
The system looks for and calls the template for "/" automatically;
other templates are only invoked via
There is a default template that gets called, if one doesn't otherwise apply:
For a text node, just return the text;
for a tag node, recursively call
You can't provide other arguments to templates — just the xpath of the element to process. And you can't call templates on any ol' node — you must specify (when you create the template) what xpaths it applies to.
<xsl:apply-templates select="ancient_wonders/wonder"> <xsl:sort select="height" order="descending" data-type="number" /> </xsl:apply-templates> |
Similar to how a web-page (data) can have a css-stylesheet directive (processing), XML data files have a xml-stylesheet tag, with what XSLT file processes them.
It's annoying, that the
// The file children.php: <?xml-stylesheet type="text/xsl" href="children.xsl"?> <?php require('children-data.xml'); ?> |
That is: Invoking XSLT via php.
So far, the XSLT we've seen is running client-side:
the client requests the .xml file,
it sees the
Note: If you run locally, not using a web-server at all, and opening via Open File…: Firefox works, but Chrome won't5. You have to open Chrome with the command-line argument--allow-file-access-from-files : on Mac this can be done with/Applications/Google\ Chrome.app/contents/MacOS/Google\ Chrome --allow-file-access-from-files .
But of course, there's nothing inherent about doing the processing on the client. You can create a server-side .php page which, when requested, does the processing, to create the final html result:
Discussion:
You can do this in php as follows:
First, omit the
<?php // Load XML file $xml = new DOMDocument; $xml->load('xml-database.xml'); // Load XSL file $xsl = new DOMDocument; $xsl->load('xsl-program.xsl'); // Configure the transformer $proc = new XSLTProcessor; // Attach the xsl rules $proc->importStyleSheet($xsl); echo $proc->transformToXML($xml); ?> |
a missed opportunity?:Observe, in the last three lines, that you can can make one
XSLTProcessor object which transforms many XML files, all using the same stylesheet.If you're only serving a single web-page, this is usually moot. It would be useful if you have php prorams that pre-generate many pages. But I can't help wondering: which use-case is more common?
A built-in for helping with the former seems much more likely to be useful; as it is, we have to fall back to doing this manually.
- You have one XML file (“database”), and you want to create several views of it (run several different XSLT programs on the same data).
- You have one view (XSLT program), and want to apply it to many XML files. Those XML files must all have the same/similar tags, since XSLT programs are highly coupled to the tags they process.
home—lects—hws
D2L—breeze (snow day)
©2017, Ian Barland, Radford University Last modified 2017.Nov.15 (Wed) |
Please mail any suggestions (incl. typos, broken links) to ibarlandradford.edu |