Transcript xsl:key

XSLT
Part 3B
id()
• The id() function returns a node-set containing the
node or nodes with a given ID attribute.
• An ID attribute in this context is any attribute
declared in DTD as having type ID
• The id() function provides an efficient means of
locating nodes given the value of ID attribute.
• Example:
<xsl:for-each select=“document(‘example.xml’)">
<xsl:value-of select=“id(‘A123’)"/>
</xsl:for-each>
generate-id()
• The generate-id() function generates a string that
uniquely identifies a node.
• Generate-id() can be used to create links in
documents.
• Generate-id() can be used to compare whether two
nodes are identical.
Keys
• The ID/IDREF mechanism for locating elements in XML
documents has been generalized in XSL to the notion of keys.
• ID/IDREF is only useful when:
– The document has declarations that identify the ID and
IDREF attributes and
– The processor is capable of processing the declarations
• Using select expressions (XPath) to locate elements may be
inefficient
• Declaring keys gives the stylesheet processor an indication of
what elements should be cached for fast access.
Club.xml
<?xml version="1.0"?>
<Club>
<Member id="1">
<Name>Smith</Name>
<Phone type="home">555-1111</Phone>
<Phone type="work">222-1212</Phone>
</Member>
<Member id="2">
<Name>Jones</Name>
<Phone type="home">123-4567</Phone>
<Phone type="work">222-7777</Phone>
</Member>
<Member id="3" >
<Name>Boggs</Name>
<Phone type="home">323-7892</Phone>
<Phone type="work">222-4567</Phone>
</Member>
</Club>
Examples
<xsl:output method="html"/>
<xsl:template match="/">
<HTML><HEAD <TITLE>Club Members</TITLE></HEAD><BODY><TABLE border="1" width="25%">
<TR><TH>Name</TH></TR>
<xsl:for-each select="/Club/Member">
<TR>
<TD>
<A href="#{generate-id()}">
<xsl:value-of select="Name"/>
</A>
</TD>
</TR>
</xsl:for-each>
</TABLE>
….
<TABLE border="1" width="25%">
<TR><TH>Home Phone Number</TH></TR>
<xsl:for-each select="/Club/Member">
<TR>
<TD>
<A name="{generate-id()}">
<xsl:value-of select="Phone[@type='home']"/>
</A>
</TD>
</TR>
</xsl:for-each>
</TABLE>
</BODY>
</HTML>
</xsl:template>
</xsl:stylesheet>
Output
<HTML>
<HEAD>
<meta http-equiv="Content-Type" content="text/html;
charset=utf-8">
<TITLE>Club Members</TITLE>
</HEAD>
<BODY><TABLE border="1" width="25%">
<TR><TH>Name</TH>
</TR>
<TR><TD><A href="#d0e4">Smith</A></TD></TR>
<TR><TD><A href="#d0e16">Jones</A></TD></TR>
<TR><TD><A href="#d0e28">Boggs</A></TD>
</TR>
</TABLE><BR><BR><BR><BR><BR><BR><BR><BR><BR><BR><TABLE
border="1" width="25%">
<TR>
<TH>Home Phone Number</TH>
</TR>
<TR><TD><A name="d0e4">555-1111</A></TD></TR>
<TR><TD><A name="d0e16">123-4567</A></TD></TR>
<TR<TD><A name="d0e28">323-7892</A></TD></TR>
</TABLE>
</BODY>
xsl:key
• Xsl:key is a top-level element used to declare a named key,
for use with the key() functions in expressions and patterns.
• Global variables cannot be used in defining the key
• The name attribute specifies the name of the key.
• The match attribute is a pattern – specifies the nodes to which
the key value applies. If a node matches the pattern, then the
node will have zero or more values for the named key, as
determined by use attribute.
• Example:
<xsl:key name="prodId" match="product" use="@code"/>
Species an expression to determine the value or values of the key
Key()
• The key() function is used to find the nodes with a given value
for a named key.
• It is used in conjunction with the <xsl:key> element.
• The key() function is provided to make associative access to
nodes more convenient and more efficient.
• Because keys provide an efficient way of retrieving all the
nodes that share a common value, they are used to group nodes
with common values.
Using an attribute for a key value
<?xml version="1.0" ?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<!-- Using an attribute for a key value -->
<xsl:key name="prodId" match="product" use="@code"/>
<xsl:template match="/">
<html>
<body>
<xsl:variable name="prodName"
select="key('prodId','7777')"/>
<p>
Name: <xsl:value-of select="$prodName/description"/><br />
Price: <xsl:value-of select="$prodName/price"/><br />
</p>
</body>
</html>
</xsl:template>
</xsl:stylesheet>
Using an element for a key value
<?xml version="1.0" ?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<!-- Using an element as a key -->
<xsl:key name="courseKey" match="course" use="courseId"/>
<xsl:param name="cid" select="345"/>
<xsl:template match="/">
<html>
<body>
<xsl:apply-templates select="key('courseKey',$cid)"/>
</body>
</html>
</xsl:template>
</xsl:stylesheet>
Multi-valued keys
• A key can be multi-valued, in that a single node can have several values
each of which can be used to find the node independently.
• The use expression, instructor/name is a node-set expression, so the string
value of each of its nodes (each instructor name) is used as one of the values
in the set of node value pairs that make up the key.
• Example
<xsl:key name="cinst" match="course“
use="instructor/name"/>
<xsl:template match="/">
<html>
<body>
<xsl:apply-templates
select="key('cinst','Smith')"/>
</body>
</html>
</xsl:template>
</xsl:stylesheet>
Course.xml
<?xml version="1.0"?>
<courses>
<course>
<title>CPP</title>
<courseId>123</courseId>
<instructor>
<name>Jones</name>
<name>Smith</name>
</instructor>
<level>Undergraduate</level>
<units>4</units>
</course>
<course>
<title>XML</title>
<courseId>345</courseId>
<instructor>
<name>Smith</name>
<name>Mills</name>
</instructor>
<level>graduate</level>
<units>2</units>
</course>
<course>
<title>Java</title>
<courseId>432</courseId>
<instructor>
<name>Boggs</name>
<name>Pratt</name>
</instructor>
<level>graduate</level>
<units>2</units>
</course>
</courses>
Output (All courses where Smith is an
instructor):
CPP 123 Jones Smith Undergraduate 4
XML 345 Smith Mills graduate 2
Example with catalog.xml, authors.xml, authors.xsl and
books.xml
<?xml version="1.0"?>
<catalog>
<book>
<title language="English">Fun With XML</title>
<author>John Robot</author>
<isbn> 12367</isbn>
</book>
<book>
<title language="English">Xml and Java</title>
<author>Mary Jones</author>
<author>John Robot</author>
<isbn> 7856</isbn>
<?xml version="1.0"?>
<authors>
<author>
<name>John Robot</name>
<country> USA</country>
</author>
<author>
<name>Mary Jones</name>
<country> UK</country>
</author>
</book>
<book>
<title language="English">C++</title>
<author>James Mills</author>
<isbn> 7777</isbn>
</book>
</catalog>
<author>
<name>James Mills</name>
<country> Australia</country>
</author>
</authors>
Authors.xsl
<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">
<xsl:key name="biog" match="author" use="name"/>
<xsl:variable name="biogs" select="document('authors.xml')"/>
<xsl:template match="/">
<HTML>
<BODY>
<xsl:variable name="allBooks" select="//book"/>
<xsl:for-each select="$allBooks">
<H1><xsl:value-of select="title"/></H1>
<xsl:for-each select="author">
<xsl:variable name="name" select="."/>
<H3><xsl:value-of select="$name"/></H3>
<xsl:for-each select="$biogs">
<xsl:variable name="auth" select="key('biog',$name)"/>
<p><xsl:value-of select="$auth/country"/></p>
<p><xsl:value-of select="$auth/biog"/></p>
</xsl:for-each>
</xsl:for-each>
</xsl:for-each>
</BODY>
</HTML>
</xsl:template>
</xsl:stylesheet>
• It is possible to have several keys for the same node.
• It is also possible to have several key definitions with the same
name.
Stylesheet Reuse via xsl:include and xsl:import
• The elements xsl:include and xsl:import enable you
to reuse other stylesheets.
• These elements are “top-level elements”. This means
that they must be immediate children of the
xsl:stylesheet element (i.e., they cannot be within a
template rule)
• The xsl:include element is basically a macro
substitution - the element is replaced by the contents
of stylesheet it references
<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">
<xsl:include href="file://localhost/xml-course/new-xsl/toUpperCase.xsl"/>
<xsl:template match="FitnessCenter">
...
</xsl:template>
...
</xsl:stylesheet>
Replace the xsl:include
element with the contents
of the referenced stylesheet
(i.e., all the children of
xsl:stylesheet)
<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">
<xsl:variable name="lcase" select="'abcdefghijklmnopqrstuvwxyz'"/>
<xsl:variable name="ucase" select="'ABCDEFGHIJKLMNOPQRSTUVWXYZ'"/>
<xsl:template match="*">
<xsl:apply-templates select="@* | * | text() | comment() | processing-instruction()"/>
</xsl:template>
<xsl:template match="@*">
<xsl:value-of select="translate(.,$lcase, $ucase)"/>
</xsl:template>
<xsl:template match="text()">
<xsl:value-of select="translate(.,$lcase, $ucase)"/>
</xsl:template>
</xsl:stylesheet>
toUpperCase.xsl
xsl:import
• xsl:import acts just like xsl:include - the stylesheet
that it references is macro-substituted. However,
there is a difference:
– With xsl:include the stuff that is macro-substituted
into the stylesheet has the same precedence as the
rest of the stylesheet. It is as though you had one
stylesheet.
– With xsl:import the stuff that is macro-substituted
into the stylesheet has lower precedence than the
rest of the stylesheet. Also, all xsl:import elements
must come first in the stylesheet.
Xsl:document
• The <xsl:document> is used to create a new output file
(introduced in XSLT1.1).
• The facility allows transformation to produce multiple output
files.
• When the xsl:document instruction is instantiated, a new
result tree is created and the new result tree becomes the
current output destination for all the nodes output until the
end of xsl:document element.
• The location of the new output file is determined by the
value of href attribute. This attribute is mandatory.
• The href may contain an absolute or relative (to the parent
document) URI.
xsl:document
<xsl:template match="Member">
<Member>
<xsl:copy-of select="Name"/>
<xsl:apply-templates select="Phone"/>
</Member>
</xsl:template>
<xsl:template match="Phone">
<xsl:variable name="phonebook" href="PhoneNumbers.xml"/>
<Phone href="{$phonebook}"/>
<xsl:document href="PhoneNumbers.xml">
<xsl:copy-of select="."/>
</xsl:document>
</xsl:template>
</xsl:stylesheet>
What is the output on the input file, club.xml
Input: Club.xml
<?xml version="1.0"?>
<Club>
<Member>
<Name>Smith</Name>
<Phone type="home">555-1111</Phone>
<Phone type="work">222-1212</Phone>
</Member>
<Member>
<Name>Jones</Name>
<Phone type="home">123-4567</Phone>
<Phone type="work">222-7777</Phone>
</Member>
<Member>
<Name>Boggs</Name>
<Phone type="home">323-7892</Phone>
<Phone type="work">222-4567</Phone>
</Member>
</Club>
Function- document()
• The most common usage of document() is to access a
document referenced from the source document (typically in
an attribute such as href).
• The document() finds an external XML document by resolving
an URI reference, parses the XML into a tree structure and
returns its tree node.
• Example:
document(“example.xml”) looks for file example.xml in the
same directory as the stylesheet, parses it and returns the root
node of the resulting tree.
document()
<?xml version="1.0" ?>
<xsl:stylesheet version="1.1"
xmlns:xsl="http://www.w3.org/
1999/XSL/Transform">
<xsl:template match="paper">
<xsl:for-each
select="reference">
<h2>Reference: <xsl:valueof select="@title"/></h2>
<xsl:apply-templates
select="document(@link)"/>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
<?xml version="1.0"?>
<catalog>
<paper >
<reference title="Logic In
Maintenance" link="review1.xml"/>
<reference title="XSLT Design
Patterns" link="review2.xml"/>
</paper>
</catalog>
Output:
Reference: Logic In Maintenance
This is a review of Logic in Maintenance Reference:
XSLT Design Patterns
This is a review of XSLT Design Patterns
Extension Elements
• The XSL processor understands how to process
xsl:template, xsl:apply-templates, xsl:if, xsl:for-each, etc
– That is, it understands the vocabulary in the XSL
namespace
• XSL Processor implementers oftentimes provide additional
elements that you may use in your stylesheet
– These extension elements will belong to a namespace
defined by the implementer
• The functions available in XPath cover only basic
functionality.
• Sometimes, you may want to invoke code written in other
languages from your stylesheet.
• The draft XSLT 1.1 specification defines a general mechanism
for calling extension functions written in any language, and
defines detailed interfaces for Java and Javascript.
• Interfaces for other languages may be defined by independent
vendors.
When are extension functions needed
• To get data held in a database
• You may need to access services that are not directly
available in XSLT or XPath.
• To perform complex operations that is cumbersome in
XSLT.
Function-available()
• Using function-available() to test whether a particular function is available
for use.
• Example:
<?xml version="1.0" ?>
<xsl:stylesheet version="1.1"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<xsl:if test="function-available('key')">
True
</xsl:if>
</xsl:template>
</xsl:stylesheet>
Example
<xsl:stylesheet
version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:date="http://www.jclark.com/xt/java/java.util.Date
">
<xsl:template match="/">
<html>
<xsl:if test="function-available('date:to-string') and
function-available('date:new')">
<p><xsl:value-of select="date:tostring(date:new())"/></p>
</xsl:if>
</html>
</xsl:template>
</xsl:stylesheet>
Example
<xsl:stylesheet
version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:Math="http://www.jclark.com/xt/java/java.lang.Math">
<xsl:template match="/">
<html>
<xsl:if test="function-available('Math:max')">
<p><xsl:value-of
select="Math:max(number('12'),number('13'))"/></p>
</xsl:if>
</html>
</xsl:template>
</xsl:stylesheet>
Example Extension Element: instruct the xsl processor
to output to another file
• Many of the xsl processor implementers provide an extension element that
instructs the xsl processor to output the contents of the element to another
file.
– Thus, your stylesheet can generate multiple output files!
XML
XSL
Processor
XSL
Vendor-specific
• Each implementor gives the extension element a different
name:
– saxon calls it: output
– xalan calls it: write
How to use an extension element
1. Declare the namespace that the extension element belongs to:
saxon:
xmlns:saxon="http://icl.com/saxon"
xalan:
xmlns:xalan="http://org.apache.xalan.xslt.extensions.Redirect"
2. Indicate that any element that is namespace qualified by the prefix
is an extension element, i.e., it has a specific meaning and should be
processed using the implementer's code:
saxon:
extension-element-prefixes="saxon"
xalan:
extension-element-prefixes="xalan"
3. Use the extension element:
saxon:
<saxon:output file="...">
-- anything in here will go to the file specified --</saxon:output>
xalan:
<xalan:write file="...">
-- anything in here will go to the file specified --</xalan:write>
Problem
• Write a stylesheet which outputs the platinum members in one
file, the gold members in another file, and the third file is an
index to the other two files.
platinum.xml
FitnessCenter.xml
XSL
Processor
<PlatinumMembers href="platinum.xml"/>
<GoldMembers href="gold.xml"/>
new-FitnessCenter.xsl
gold.xml
FitnessCenter.xsl
FitnessCenter.xml
<?xml version="1.0"?>
<?xml-stylesheet type="text/xsl" href="FitnessCenter.xsl"?>
<FitnessCenter>
<Member level="platinum">
<Name>Jeff</Name>
<Phone type="home">555-1234</Phone>
<Phone type="work">555-4321</Phone>
<FavoriteColor>lightgrey</FavoriteColor>
</Member>
<Member level="gold">
<Name>David</Name>
<Phone type="home">383-1234</Phone>
<Phone type="work">383-4321</Phone>
<FavoriteColor>lightblue</FavoriteColor>
</Member>
<Member level="platinum">
<Name>Roger</Name>
<Phone type="home">888-1234</Phone>
<Phone type="work">888-4321</Phone>
<FavoriteColor>lightyellow</FavoriteColor>
</Member>
</FitnessCenter>
FitnessCenter.xsl
?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:saxon="http://icl.com/saxon"
extension-element-prefixes="saxon"
version="1.0">
<xsl:output method="xml"/>
<xsl:template match="FitnessCenter">
<FitnessCenter>
<PlatinumMembers href="platinum.xml"/>
<saxon:output href="platinum.xml">
<PlatinumMembers>
<xsl:for-each select="Member[@level='platinum']">
<xsl:copy-of select="."/>
</xsl:for-each>
</PlatinumMembers>
</saxon:output>
<GoldMembers href="gold.xml"/>
<saxon:output href="gold.xml">
<GoldMembers>
<xsl:for-each select="Member[@level='gold']">
<xsl:copy-of select="."/>
</xsl:for-each>
</GoldMembers>
</saxon:output>
</FitnessCenter>
</xsl:template>
</xsl:stylesheet>
extension-element-prefixes
• The extension-element-prefixes is used to tell the xsl
processor, "whenever you encounter an element with any of
these prefixes listed here you are to treat it as an extension
element, and process it using the implementer's code"
• If you fail to do so the xsl processor will simply output the
element literally
Dynamic (run-time) Evaluation
• Many xsl processor implementers give you an extension
function that enables you to dynamically evaluate an
expression.
– That is, you can generate the expression on the fly, or read
it in from an external file.
• SAXON provides an extension function called evaluate to do
this.
Example
<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:saxon="http://icl.com/saxon"
extension-element-prefixes="saxon"
version="1.0">
<xsl:output method="xml"/>
<xsl:variable name="elementName" select="'title'"/>
<xsl:template match="/">
<xsl:variable name="check"
select="saxon:evaluate(concat('//book/',$elementName))"/>
<xsl:value-of select="$check"/>
</xsl:template>
</xsl:stylesheet>
Database Connectivity
Using Saxon
•
•
•
•
•
•
To use the SQL extension elements in a stylesheet, you need to define a namespace
prefix (for example "sql") in the extension-element-prefixes attribute of the
xsl:stylesheet element.
This extension defines four new stylesheet elements: sql:connect, sql:insert,
sql:column, and sql:close:
sql:connect creates a database connection. It has attributes "driver", "database",
"user", and "password", all of which are attribute value templates (so the values can
be passed in as parameters). The driver attribute names the JDBC driver class to be
used. The database name must be name that JDBC can associate with an actual
database, and in the sample stylesheet this database must contain a a table "Book"
with three character columns, "Title", "Author", and "Category".
sql:insert performs an SQL INSERT statement. This causes a row to be added to
the table identified by the "table" attribute.
sql:column is used as a child element of sql:insert, and identifies the name and
value of a column to be included in the INSERT statement. The name of the column
is identified by the "name" attribute, the value may be indicated either by
evaluating the expression contained in the "select" attribute, or as the expanded
contents of the sql:column element. The value is always interpreted as a String.
(Remember this is purely a demonstration of extensibility, in a real system there
would be a need to cater for SQL columns of other data types).
sql:close closes the database connection.
To populate an Access database from Books.xml using Bookssql.xsl
<xsl:stylesheet
xmlns:sql="http://icl.com/saxon/extensions/com.icl.saxon.sql.SQ
LElementFactory"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"
xmlns:saxon="http://icl.com/saxon"
extension-element-prefixes="saxon">
<!-- insert your database details here, or supply them in
parameters -->
<xsl:param name="driver" select="'sun.jdbc.odbc.JdbcOdbcDriver'"/>
<xsl:param name="database" select="'jdbc:odbc:test'"/>
<xsl:param name="user"/>
<xsl:param name="password"/>
<xsl:template match=“/">
<xsl:if test="not(element-available('sql:connect'))">
<xsl:message>sql:connect is not available</xsl:message>
</xsl:if>
<xsl:message>Connecting to <xsl:value-of
select="$database"/>...</xsl:message>
<sql:connect driver="{$driver}" database="{$database}"
user="{$user}" password="{$password}"
xsl:extension-element-prefixes="sql">
<xsl:fallback>
<xsl:message terminate="yes">SQL extensions are not
installed</xsl:message>
</xsl:fallback>
To populate an Access database from Books.xml using Bookssql.xsl
<xsl:template match=“catalog">
<xsl:for-each select=“book">
<sql:insert table="book" xsl:extension-elementprefixes="sql">
<sql:column name="title" select="TITLE"/>
<sql:column name="author"
select="AUTHOR"/>
<sql:column name="category"
select="@CAT"/>
</sql:insert>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>