More on XSLT
More on XSLT variables
• Earlier we saw two ways to associate a value with a
variable
A variable whose value is the empty string, for example
<xsl:variable name="someVariable" />
A variable whose value is specified by the select attribute
Examples
<xsl:variable name="someVariable" select="./name" />
<xsl:variable name="someVariable" select="'name'" />
• But there is a third way, which allow greater flexibility
– It involves using a non-empty xsl:variable element
– The content specifies the value of the variable
– We can use the full range of XSLT elements to compute the
value
Computing a contingent value for an xsl:variable
<?xml version="1.0"?>
<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0" >
<xsl:template match="/">
<xsl:variable name="numberOfPeople">
<xsl:choose>
<xsl:when test="count(/people/person[age>21]) > 1"> too many old people </xsl:when>
<xsl:otherwise> <xsl:value-of select="count(/people/person)"/> </xsl:otherwise>
</xsl:choose>
</xsl:variable>
<body>The number of people is: <xsl:value-of select="$numberOfPeople"/></body>
</xsl:template>
</xsl:transform>
Computing a cumulative value for an xsl:variable
<?xml version="1.0"?>
<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0" >
<xsl:template match="/">
<xsl:variable name="agesOfPeople">
<xsl:for-each select="/people/person">
<xsl:text> </xsl:text> <xsl:value-of select="age"/>
</xsl:for-each>
</xsl:variable>
<body>The ages of the people are: <xsl:value-of select="$agesOfPeople"/></body>
</xsl:template>
</xsl:transform>
Computing a complex cumulative value for an xsl:variable
<?xml version="1.0"?>
<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0" >
<xsl:template match="/">
<xsl:variable name="agesOfPeople">
<xsl:for-each select="/people/person">
<xsl:value-of select="name"/><xsl:text>:</xsl:text><xsl:value-of select="age"/>
<xsl:text> </xsl:text>
</xsl:for-each>
</xsl:variable>
<body>The ages of the people are: <xsl:value-of select="$agesOfPeople"/></body>
</xsl:template>
</xsl:transform>
Aside: the position() function
• When a sequence of items is being
processed, this function return the position
of the current item within the sequence
• This is true whether the sequence of items
is being processed by
– a for-each element, or
– an apply-templates element
Terminating a cumulative value for an xsl:variable
•
This program does not append a comma to the last item in the sequence
<?xml version="1.0"?>
<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0" >
<xsl:template match="/">
<xsl:variable name="agesOfPeople">
<xsl:for-each select="/people/person">
<xsl:value-of select="age"/>
<xsl:if test="position() < count(/people/person)"><xsl:text>, </xsl:text> </xsl:if>
</xsl:for-each>
</xsl:variable>
<body>The ages of the people are: <xsl:value-of select="$agesOfPeople"/></body>
</xsl:template>
</xsl:transform>
Aside: the last() function
• When a sequence of items is being
processed, this function return the position
of the last item within the sequence
• This is true whether the sequence of items
is being processed by
– a for-each element, or
– an apply-templates element
Terminating a cumulative value for an xsl:variable, version 2
•
This program uses last() to avoid placing a comma after last item in sequence
<?xml version="1.0"?>
<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0" >
<xsl:template match="/">
<xsl:variable name="agesOfPeople">
<xsl:for-each select="/people/person">
<xsl:value-of select="age"/>
<xsl:if test="position() < last()"><xsl:text>, </xsl:text></xsl:if>
</xsl:for-each>
</xsl:variable>
<body>The ages of the people are: <xsl:value-of select="$agesOfPeople"/></body>
</xsl:template>
</xsl:transform>
Aside: templates can be used when
computing values for variables
• We can use the full range of XSLT elements to compute
the value
• This includes using the apply-templates element
Using apply-templates to compute the value for a variable
<?xml version="1.0"?>
<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0" >
<xsl:template match="/">
<xsl:variable name="agesOfPeople">
<xsl:apply-templates select="/people/person[age > 21]"/>
</xsl:variable>
<body>The ages of the people over 21 are <xsl:value-of select="$agesOfPeople"/></body>
</xsl:template>
<xsl:template match="person">
<xsl:value-of select="age"/>
<xsl:if test="position() < count(/people/person)"><xsl:text>, </xsl:text></xsl:if>
</xsl:template>
</xsl:transform>
Aside: More on sorting
• We have seen that sort elements can be used inside foreach elements
• They can also be used inside apply-template elements
Sorting inside an apply-templates element
<?xml version="1.0"?>
<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0" >
<xsl:template match="/">
<xsl:variable name="agesOfPeople">
<xsl:apply-templates select="/people/person">
<xsl:sort select="name"/>
</xsl:apply-templates>
</xsl:variable>
The ages of the sorted people are <xsl:value-of select="$agesOfPeople" />
</xsl:template>
<xsl:template match="person">
<xsl:value-of select="age"/><xsl:if test="position() < count(/people/person)"><xsl:text>, </xsl:text>
</xsl:if>
</xsl:template>
</xsl:transform>
The xsl:copy-of element
• The xsl:value-of element returns the
string value of the selected node (set)
• The xsl:copy-of element returns a copy of
the XML Fragments for the selected
node (set)
• This difference is usually not visible when
we use client-side XSLT processing
– because browsers do not render XML tags
that they have generated themselves
The difference between xsl:copy-of and xsl:value-of is
(usually) invisible when XSLT is applied on the client-side
The difference between xsl:copy-of and xsl:value-of is also invisible
when a browser renders result of server-side HTML generation
But the difference between xsl:copy-of and xsl:value-of is visible when
a browser shows source code from server-side HTML generation
Now, consider XML generation on the server-side
• On the last two slides, the server sent to the browser the default
Content-Type header that is sent when PHP programs are
executed, that is Content-Type: text.html
• Thus, in both examples on each slide, the text in the message
bodies was treated by the browser as HTML text
• On the next slide, the PHP program specifies that a different header
Content-Type: text.xml
• As a result of this different header, the browser will treat the text in
the message bodies as XML text
• Because of this different header,
– even though the text of the messages bodies is the same as on the previous
slides,
– The browser will show something different than we saw on t he previous slides
Difference between xsl:copy-of and xsl:value-of is visible when a
browser renders result of server-side XML generation
Difference between xsl:copy-of and xsl:value-of (contd.)
• Remember that, when a node set is converted to a
string, only the string value of the first node in the set is
returned
• This means that, when a browser renders the result,
more text is visible when xsl:copy-of is used to process a
node-set than when xsl:value-of is used
– that is, some difference, at least, is visible in the browser
– even though the XML tags returned by xsl:copy-of are ignored by
the browser
• We will see this on the next slide
When a node set is selected, more text is visible from xsl:copy-of than from
xsl:value-of , even in client-side processing
Common usage of xsl:copy-of
• One common usage of xsl:copy-of is
– generating another XML document from an existing
one
• An extension of this is
– generating multiple alternative XML document(s) from
an existing one
• These operations could be done using clientside processing
• But it makes more sense to use server-side
processing
– because XML generated on the client-side is not
given the default tree-based rendering by the browser
Generating two alternative XML document(s) from an
existing one, depending on the query-string in the request
Creating XML with the xsl:element element
• The stylesheets on the previous slides used literal tags,
like this
<ages> ... </ages>
• Instead of using literal tags, we can use the xsl:element
element, like this
<xsl:element name="ages"> ... </xsl:element>
• That is, two stylesheets below produce the same result
Combining input from multiple XML documents
• We have seen how a PHP program can use different XSLT
stylesheets to extract different pieces of information from one XML
document
that is, an XML document can be split into multiple other
documents
• XSLT provides a function which we can use to do the reverse - to
combine multiple XML documents into one output document
the document() function can be used to access nodes in an
external XML document
• If the final document is to be in HTML, we can use this function in
client-side processing
• But, if the final document is to be in XML, it makes more sense to
use the function in server-side processing
• We will consider some examples on the next few slide
The two XML documents that we will combine
• One document contains the ages of some people
• The other document contains the names of these people
• The two documents can be cross-referenced because
each person is identified by an ID
Combining two XML documents into HTML, using the
document() function in client-side XSLT processing
<?xml version="2.0"?>
<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0" >
<xsl:template match="/">
<html> <body> <table rules="all"> <thead> <tr><th>Name</th><th>Age</th></tr> </thead> <tbody>
<xsl:for-each select="ages/age">
<xsl:variable name="thisPersonID" select="@personID"/>
<tr>
<td>
<xsl:value-of select="document('names.xml')/names/name[@personID=$thisPersonID]"/>
</td>
<td> <xsl:value-of select="."/> </td>
</tr>
</xsl:for-each>
</tbody> </table> </body></html>
</xsl:template>
</xsl:transform>
Combining two XML documents into XML, using the document()
function in server-side XSLT processing
<?xml version="1.0"?>
<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0" >
<xsl:template match="/">
<people>
<xsl:for-each select="ages/age">
<xsl:variable name="thisPersonID" select="@personID"/>
<person>
<name>
<xsl:value-of select="document('names.xml')/names/name[@personID=$thisPersonID]"/>
</name>
<age> <xsl:value-of select="."/> </age>
</person>
</xsl:for-each>
</people>
</xsl:template></xsl:transform>
Creating processing instructions using the
<xsl:processing-instruction> element
• Syntax of this element:
<xsl:processing-instruction name= "process-name" >
<!-- Content -->
</xsl:processing-instruction>
• Example usage:
<xsl:processing-instruction name= "xml-stylesheet" >
type="text/xsl" href="someSheet.xsl"
</xsl:processing-instruction>
creates
<?xml-stylesheet type="text/xsl" href="someSheet.xsl" ?>
Server-side combination of XML documents into XML that is processed
by XSLT on the client-side
<?xml version="1.0"?><xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0" >
<xsl:template match="/">
<xsl:processing-instruction name= "xml-stylesheet" >
type="text/xsl" href="people.xsl"
</xsl:processing-instruction>
<people> <xsl:for-each select="ages/age"><xsl:variable name="thisPersonID" select="@personID"/>
<person><name><xsl:value-of select="document('names.xml')/names/name[@personID=$thisPersonID]"/>
</name><age> <xsl:value-of select="."/> </age></person></xsl:for-each></people>
</xsl:template></xsl:transform>
XML Fragments
XML Fragments
• Consider, again, this XSLT element
<xsl:copy-of select="/people"/>
• The select attribute identifies a set of nodes in a parse
tree
• But the xsl:copy-of element does NOT return a copy of
the selected nodes
• Instead, it returns an XML Fragment for the selected
node set
• This distinction - between a node set and an XML
Fragment - is very important
• But, first, let's see some more about XML Fragments
– because, as we shall see later, the fact that XSLT can create
XML Fragments gives us great power
XML Fragments
• An XML Fragment is a portion of an XML document
• An XML Fragment may not be well-formed
For example, it may not have a single root document,
as in this fragment
<thing>car</thing><thing>bike</thing>
• Usually, however, an XML Fragment is well-balanced.
Informally, this means that, if the fragment includes
any part of the markup of any construct, it contains all
of the markup of that construct
For example, if a well-balanced fragment contains
part of an element, it contains all of the start tag and
all of the end tag for the element
• For more detail on XML Fragments, see
http://www.w3.org/TR/xml-fragment
Fragments versus nodes: errors in XPath expressions
<?xml version="1.0"?>
<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:template match="/">
<xsl:variable name="thePeople"> <xsl:copy-of select="/people"/> </xsl:variable>
<body>The number of people is <xsl:value-of select="count($thePeople/people/person)"/></body>
</xsl:template>
</xsl:transform>
• This program causes an error because the value given to the variable
$thePeople is
<people><person><name>Fred</name><age>21</age></person><person><name>Tom</name><age>22</age></person><
person><name>Dick</name><age>23</age></person></people>
• This is an XML fragment
• It is a piece of unparsed text,
• But, in this place, XPath requires a set of parse tree nodes
Fragments versus nodes: errors in XPath expressions (contd.)
• Later, we shall see the how useful it would be to be able to do
something like this
<xsl:variable name="thePeople">
<xsl:copy-of select="/people"/>
</xsl:variable>
...
<xsl:value-of select="count($thePeople/people/person)"/>
• So the problem caused by an XML Fragment appearing
where a node-set is expected is something we need to
overcome
• We can do this using the XSLT extension mechanism
XSLT extension mechanism
• The W3C specification for XSLT 1.0 is at
http://www.w3.org/TR/xslt
• It defined a mechanism for adding new instruction
elements and functions to the language. This
mechanism is specified at
http://www.w3.org/TR/xslt#extension
XSLT extension mechanism (contd.)
• Many sets of extension elements and functions have been
developed
• Using a set of extensions developed by somebody else is easy
• We simply
make the stylesheet refer to the namespace for the set of
extensions, and
use the prefix we associated with the namespace each time that
we use an extension element/function
<?xml version="1.0"?>
<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"
xmlns:somePrefix="someNamespace" >
...
<xsl:value-of select="count(somePrefix:someFunction(. . .)"/>
...
</xsl:transform>
XSLT extension mechanism (contd.)
• Various s/w developers produced different proprietary
sets of extension elements and functions
• This caused problems because stylesheets which used
these proprietary extensions were not portable
• To address this portability problem, a group was formed to identify a
set of extensions that a large number of people thought were
needed
• The identified set of extensions is called EXSLT and is defined at
http://www.exslt.org/
• Because this set of extensions was widely accepted, several
developers of XSLT processors claim to have implemented them
EXSLT
• The EXSLT extensions are divided into a group of modules, each of
which has its own namespace
– Common EXSLT, http://exslt.org/common
– Math EXSLT, http://exslt.org/math
– Sets EXSLT, http://exslt.org/sets
– Date and Times EXSLT, http://exslt.org/dates-and-times
– Strings EXSLT, http://exslt.org/strings
– Regular Expressions EXSLT, http://exslt.org/regular-expressions
– Dynamic EXSLT, http://exslt.org/dynamic
– Random EXSLT, http://exslt.org/random
– Functions EXSLT, http://exslt.org/functions
(allow users to define their own functions for use in expressions and patterns in
XSLT)
• Unfortunately, not every XSLT processor which claims to support
EXSLT supports all of the above
– Luckily, however, all/most of them support the extension function which
we need when we want to use XML fragments in XPath expressions
The node-set() function in EXSLT
• One of the extension functions included in the Common EXSLT
module is called
node-set()
• It takes a well-balanced XML fragment as input, parses it, and
returns a set of nodes
• We can use it in an improved version of our last stylesheet
The EXSLT node-set() extension function
<?xml version="1.0"?>
<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"
xmlns:exsl="http://exslt.org/common" >
<xsl:template match="/">
<xsl:variable name="thePeople"><xsl:copy-of select="/people"/> </xsl:variable>
<body>
The number of people is <xsl:value-of select="count(exsl:node-set($thePeople)/people/person)"/>
</body>
</xsl:template>
</xsl:transform>
• The node-set() function computes a parse tree from an XML fragment
• We can then use this parse tree in XPath, just as we would use any subtree of the parse tree for the XML document being processed by the
stylesheet
Not all browsers support EXSLT
• The EXSLT node-set() function is implemented by several
Firefox, Chrome, Safari and Opera, but not by MSIE
In the last screenshot below, MSIE claims that there are
no functions in the namespace http://exslt.org/common
Although MSIE does not support EXSLT node-set(), it provides an
equivalent function of the same name, in a different namespace
<?xml version="1.0"?>
<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"
xmlns:msxsl="urn:schemas-microsoft-com:xslt" >
<xsl:template match="/">
<xsl:variable name="thePeople"><xsl:copy-of select="/people"/></xsl:variable>
<body>
The number of people is <xsl:value-of select="count(msxsl:node-set($thePeople)/people/person)"/>
</body>
</xsl:template>
</xsl:transform>
• The namespace for XSLT extensions in MSIE is
urn:schemas-microsoft-com:xslt
Output generated by stylesheet refers to the XSLT extension namespace
•
•
When the server receives a request
for our document, demo12.xml, its
response contains XML
But, our stylesheet, demo12.xsl, is
also generating XML
– the <body> and </body> tags are not
in the HTML namespace
•
•
•
Each browser takes its own
approach to rendering the resultant
XML
Most browsers take the standard
approach of just rendering the string
value of the resultant XML
But the Opera browser complains
that the resultant XML has no style
information and renders the actual
resultant XML fragment
– we see something interesting - the
resultant XML refers to the
namespace for the XSLT extension
function
Preventing namespace references in generated XML output
•
•
The XSLT extension mechanism includes a tool to prevent the generation of
references to extension namespaces
This is an attribute called extension-element-prefixes which should be used in the
root element of the stylesheet
the attribute's value is a list of prefixes for the spaces which should not be referenced
When we use it, Opera shows that the EXSLT Common namespace is not cited
(although it still complains that there is no XSLT stylesheet)
<?xml version="1.0"?>
<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"
xmlns:exsl="http://exslt.org/common" extension-element-prefixes="exsl" >
<xsl:template match="/">
<xsl:variable name="thePeople"><xsl:copy-of select="/people"/></xsl:variable>
<body>The number of people is
<xsl:value-of select="count(exsl:node-set($thePeople)/people/person)"/>
</body>
</xsl:template>
</xsl:transform>
Of course, if we generate HTML on the client-side, we get no warning,
even from Opera, about the lack of an XSLT stylesheet
<?xml version="1.0"?>
<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"
xmlns:exsl="http://exslt.org/common" extension-element-prefixes="exsl" >
<xsl:output method="html"/>
<xsl:template match="/">
<xsl:variable name="thePeople"><xsl:copy-of select="/people"/></xsl:variable>
<body>The number of people is
<xsl:value-of select="count(exsl:node-set($thePeople)/people/person)"/>
</body>
</xsl:template>
</xsl:transform>
XSLT extension functions as a motivation for
server-side processing
• We have seen that MSIE does not support EXSLT
(although it supports something which is probably
functionally equivalent)
• It is also the case that even those browsers which do
support EXSLT do not support all of EXSLT
– for example, Firefox does not support all of the Math
extension functions defined in EXSLT
• Consequently, some website developers prefer to
achieve browser compatibility by using server-side
processing
EXSLT support in PHP
• We have seen that we can perform XSLT processing on the server
with PHP
• PHP5 supports EXSLT, although some systems administrators do not
turn it on
• But, the XSLT module in PHP5 provides a function for checking if
EXSLT support has been turned on
<?php
$proc = new XSLTProcessor;
if ( !$proc->hasExsltSupport() )
{ die('EXSLT support not available');
}
// use EXSLT here if the program has not died above
...
?>
• If we are sure that XSLT support has been turned on
we don't need to use the hasExsltSupport() function
we just have to use PHP module for XSLT processing as before
• the only reference to EXSLT is in the XSLT stylesheet
Server-side usage of node-set function from EXSLT
•
•
•
The XSLT generates
HTML4,
– so the PHP
program does not
set an XML
header
The XSLT uses nodeset() from EXSLT to
process an XML
fragment in an XPath
expression
Because EXSLT is
used on the server,
the approach is
compatible with all
HTML4-compliant
browsers
– see next slide
Server-side usage of EXSLT on
previous slide is compatible
with all HTML4-compliant
browsers
The power of using XML
fragments in XSLT
The power of using XML fragments in XSLT
• We have seen that EXSLT means we can
use XML Fragments in XSLT
• This enables us to simplify many XSLT
processing tasks that, otherwise, would be
quite complicated
• For example, if we need to process an
XML document that has several layers of
cross-referencing, we can "flatten" the
XML before we do the required processing
Using node-set() means that
we can apply the full power of XSLT processing
to the content of XML Fragments
• Applying node-set() to an XML fragment constructs a parse tree for
the fragment
• In effect, the XML fragment becomes a "mini-document" which we
can process using the full power of XSLT
• Suppose, for example, we bind $someVariable to an XML fragment
• We can, for example, do any of the following
<xsl:apply-templates select="exsl:node-set($thePeople)/people/person"/>
<xsl:for-each select="exsl:node-set($thePeople)/people/person">
...
</xsl:for-each>
Applying for-each to content of an XML fragment
• This stylesheet has one
template, which matches
the root node
• It binds the variable
$thePeople to an XML
fragment
• Then it uses node-set() to
compute a parse tree from
this XML fragment and ...
• ... uses a for-each element
to process each person
node within this tree
Applying templates to content of an XML fragment
• This stylesheet has two
templates
• The template which
matches the root node
binds the variable
$thePeople to an XML
fragment
• Then it uses node-set() to
compute a parse tree from
this XML fragment and ...
• ... uses the second
template to process each
person node within this
tree
Interpretation of / while processing fragment parse trees
• The interpretation of absolute XPath expressions changes during the
processing of parse trees for XML fragments
• While XSLT is processing the parse tree of an XML fragment,
– the fragment is treated, almost, like an XML document in its own right
• Specifically, the parse tree for the fragment becomes the context for
the processing
• This means that, while the parse tree for the fragment is being
processed, absolute XPath expressions refer to this parse tree,
rather than to the parse tree for the overall XML document
That is, the meta-character / at the start of an XPath expression refers to
the root of the parse tree for the fragment
• On the next few slides, we will see several examples of this,
involving for-each and apply-templates
• Later, when we use XML fragments to process complex documents,
we will see the importance of using / with care
Example 1 (using for-each), version 1
•
•
•
This stylesheet binds
$theFirstnames to an XML
fragment for the <firstnames>
element
It then uses node-set() to
compute a parse tree from this
XML fragment and tries to output
the age of each person whose
firstname is in the fragment
No age is output because, in the
XPath expression
/info/ages/age[..]
the root node is actually the
root node of the parse tree
for $theFirstnames
– which contains no age
elements
Example 1 (using for-each), version 2
•
•
•
As before the stylesheet binds
$theFirstnames to an XML
fragment for the <firstnames>
element and uses node-set() to
compute a parse tree from this
XML fragment
Before it uses for-each on each
firstname in this parse tree, it
stores a copy of the tree for the
overall document in
$mainDocument
Ages are output this time
because, in the XPath
expression
$mainDocument/info/ages/age[..]
the root node is the root node of
the parse tree for the overall
document - which does
contain age elements
Showing that the interpretation of / changes
•
•
•
•
•
As before, this stylesheet binds
$theFirstnames to an XML
fragment for the <firstnames>
element and uses node-set() to
compute a parse tree from this
XML fragment
Before it uses for-each on each
firstname in this parse tree, it prints
the tagname of the root element for
the overall document
Inside the for-each statement, it
prints the tagname of the current
root element
We can see they are different
This is because, inside the foreach, the root element is the root
element of
exsl:node-set($theFirstnames)
Example 2 (using apply-templates), version 1
•
•
•
•
There are two templates
The first one binds $fred to an
XML fragment for the
<firstname> element whose
pid=1 and uses apply-templates
to process the root element in
the parse tree for $fred
The second template tries to
output the age of the person
cited in this element
But no age is output because, in
the XPath expression
/info/ages/age[..]
the root node is actually the
root node of the parse tree
for $fred
– which contains no age
elements
Example 2 (using apply-templates), version 2
•
•
•
•
Again, there are two templates
As before, the first one binds
$fred to an XML fragment for the
<firstname> element whose
pid=1 and uses apply-templates
to process the root element in
the parse tree for $fred
The second template does
output the age of the person
cited in this element
An age is output because, in the
XPath expression
$mainDocument/info/ages/age[..]
$mainDocument contains the
parse tree for the main
document - which does
contain age elements
A note on variable scope
•
•
•
•
•
In the program on the last slide,
the value of $mainDocument is
used only in the second
template
But $mainDocument could not
be set inside this template
because, there, the meta
character / refers to the root note
of $fred
And $mainDocument could not
be set in the first template,
because it would not be visible in
the second template
So, $mainDocument had to be
set outside any template - in the
transform element
Note that when a variable is set
at the level of the transform
element its ordering, relative to
the templates, is not important
Applying XSLT processing to XML fragments
• Being able to apply XSLT processing to XML fragments
gives us a lot of power
• It means that, if we have a complicated XML document,
we can compute a simpler abstract from the given
document
• Then, if necessary, we can compute an even simpler
abstract from the first abstract
• And so on, ...
... until, after possibly several levels of abstraction, ...
... we have an XML fragment that is simple to process
To put it another way, …
• Being able to assign a variable to an XML
fragment means that, in effect, we can
have XSLT variables whose values are
– data structures, rather than …
– … just scalar values, like numbers and strings
Repeated abstraction - an example
• Consider this XML document
• Suppose we want a stylesheet
which will identify and display
the name of the oldest person
born in the city in which most
people were born
• This person's name is Bob
• The task of computing this can
be simplified by using XML
fragments to perform repeated
abstraction
Example of repeated abstraction (contd.)
• We will use server-side processing
• The PHP program is below
• We just need to write the stylesheet
Example of repeated abstraction (contd.)
• Task: identify name of oldest
person born in city in which
most people were born
• Steps
– Compute numbers of people
born in each city
– Identify city in which most
people were born
– Get IDs of people who were
born in this city
– Get ages of these people
– Identify highest age among
these ages
– Using the pid of this age get
the name of this person
Example of repeated abstraction (contd.)
• Step 1: Compute numbers of
people born in each city and
put in $cityBirths
Example of repeated abstraction (contd.)
• Step 2: From $cityBirths, compute
$IdOfCityWithMostBirths
Example of repeated abstraction (contd.)
Step 3: From $IdOfCityWithMostBirths, compute
$IdsOfPeopleBornInCityWithMostBirths
Example of repeated abstraction (contd.)
Step 4: From $IdsOfPeopleBornInCityWithMostBirths, compute
$agesOfPeopleBornInCityWithMostBirths
• Note the need to remember the primary document in a variable
<xsl:variable name="agesOfPeopleBornInCityWithMostBirths">
<list>
<xsl:variable name="primaryDoc" select="/"/>
<xsl:for-each
select="exsl:nodeset($IdsOfPeopleBornInCityWithMostBirths)/personIds/personId">
<item>
<xsl:attribute name="pid">
<xsl:value-of select="."/>
</xsl:attribute>
<xsl:value-of select="$primaryDoc/info/ages/age[@pid=current()]"/>
</item>
</xsl:for-each>
</list>
</xsl:variable>
Example of repeated abstraction (contd.)
Step 5: From $agesOfPeopleBornInCityWithMostBirths compute
$IdOfOldestPersonBornInCityWithMostBirths
List these ages in descending order, but
output only the pid attribute of the first member of the sorted set
<xsl:variable name="IdOfOldestPersonBornInCityWithMostBirths">
<xsl:for-each
select="exsl:node-set($agesOfPeopleBornInCityWithMostBirths)/list/item">
<xsl:sort select="." order="descending"/>
<xsl:if test="position()=1"><xsl:value-of select="./@pid"/></xsl:if>
</xsl:for-each>
</xsl:variable>
Example of repeated abstraction (contd.)
Step 6: From $IdOfOldestPersonBornInCityWithMostBirths compute the name of
the person with this pid
<p>
The name of the oldest person born in the city with most births is
<xsl:value-of
select="/info/firstnames/firstname[@pid =
exsl:node-set( $IdOfOldestPersonBornInCityWithMostBirths )]"/>
</p>
Complete stylesheet, part 1
<?xml version="1.0"?>
<xsl:transform
xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"
xmlns:exsl="http://exslt.org/common" >
<xsl:output method="html" version="4.0" indent="yes"/>
<xsl:template match="/">
<html>
<head><title>Oldest person in most populous city</title></head>
<body>
<xsl:variable name="cityBirths">
<cityBirths>
<xsl:for-each select="/info/citynames/cityname">
<xsl:variable name="cityID" select="@cid"/>
<cityBirth>
<cityID> <xsl:value-of select="$cityID"/> </cityID>
<numBirths> <xsl:value-of select="count(/info/birthplaces/bornIn[city=$cityID])"/> </numBirths>
</cityBirth>
</xsl:for-each>
</cityBirths>
</xsl:variable>
Complete stylesheet, part 2
<xsl:variable name="IdOfCityWithMostBirths">
<xsl:for-each select="exsl:node-set($cityBirths)/cityBirths/cityBirth">
<xsl:sort select="numBirths" order="descending"/>
<xsl:if test="position()=1">
<xsl:value-of select="cityID"/>
</xsl:if>
</xsl:for-each>
</xsl:variable>
<xsl:variable name="IdsOfPeopleBornInCityWithMostBirths">
<personIds>
<xsl:for-each select="/info/birthplaces/bornIn[city=$IdOfCityWithMostBirths]/person">
<personId>
<xsl:value-of select="."/>
</personId>
</xsl:for-each>
</personIds>
</xsl:variable>
Complete stylesheet, part 3
<xsl:variable name="agesOfPeopleBornInCityWithMostBirths">
<list>
<xsl:variable name="primaryDoc" select="/"/>
<xsl:for-each select="exsl:node-set($IdsOfPeopleBornInCityWithMostBirths)/personIds/personId">
<item>
<xsl:attribute name="pid">
<xsl:value-of select="."/>
</xsl:attribute>
<xsl:value-of select="$primaryDoc/info/ages/age[@pid=current()]"/>
</item>
</xsl:for-each>
</list>
</xsl:variable>
Complete stylesheet, part 4
<xsl:variable name="IdOfOldestPersonBornInCityWithMostBirths">
<xsl:for-each select="exsl:node-set($agesOfPeopleBornInCityWithMostBirths)/list/item">
<xsl:sort select="." order="descending"/>
<xsl:if test="position()=1">
<xsl:value-of select="./@pid"/>
</xsl:if>
</xsl:for-each>
</xsl:variable>
<p>
The name of the oldest person born in the city with most births is
<xsl:value-of select="/info/firstnames/firstname[@pid=exsl:node-set($IdOfOldestPersonBornInCityWithMostBirths)]"/>
</p>
</body>
</html>
</xsl:template>
</xsl:transform>
© Copyright 2025 Paperzz