440 likes | 607 Views
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.
E N D
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>
<?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 Course.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> </book> <book> <title language="English">C++</title> <author>James Mills</author> <isbn> 7777</isbn> </book> </catalog> <?xml version="1.0"?> <authors> <author> <name>John Robot</name> <country> USA</country> </author> <author> <name>Mary Jones</name> <country> UK</country> </author> <author> <name>James Mills</name> <country> Australia</country> </author> </authors> Example with catalog.xml, authors.xml, authors.xsl and books.xml
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> <?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> Replace the xsl:include element with the contents of the referenced stylesheet (i.e., all the children of 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.
<?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:value-of 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 document()
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:to-string(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! XSL Processor XML 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 XSL Processor <PlatinumMembers href="platinum.xml"/> <GoldMembers href="gold.xml"/> FitnessCenter.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 ConnectivityUsing 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 Books-sql.xsl <xsl:stylesheet xmlns:sql="http://icl.com/saxon/extensions/com.icl.saxon.sql.SQLElementFactory" 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> </sql:connect>
To populate an Access database from Books.xml using Books-sql.xsl <xsl:template match=“catalog"> <xsl:for-each select=“book"> <sql:insert table="book" xsl:extension-element-prefixes="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>