Subject: Re: [xsl] XSLT pointers / questions From: Michael Kay <mike@xxxxxxxxxxxx> Date: Tue, 21 Sep 2010 22:38:27 +0100 |
Question 1: $result-nodes gets created in document order for all browsers but the Webkit browsers Chrome and Safari. Is it correct that Webkit does not generate the nodes in document order?
Question 2: Can xsl:copy-of help making Chrome and Safari behave like the other browsers and generate $result-nodes in document order? http://www.w3.org/TR/xslt#copy-of
Michael Kay Saxonica
The sample input XML document can be found here: http://www.stamm-wilbrandt.de/en/xsl-list/xsltPointers/sevennodetypes.xml
<?xml-stylesheet type="text/xsl" href="demo.xsl"?><!--comment--> <e1 att="a">testtext <e2 xmlns:m="urn:m" xmlns:ns="urn:n"> <e3 xmlns="urn:d" /> <e4 xmlns:ns="urn:n2" /> </e2></e1>
Below text (A) gets generated by clicking on sevennodetypes.xml.
The stylesheet (B) below can be found here: http://www.stamm-wilbrandt.de/en/xsl-list/xsltPointers/demo.xsl
(A) XSLT pointers (Referencing) Every non-namespace node will be represented by: <id><gen><xsl:value-of select="generate-id()"/></gen></id> This allows to dereference the pointers by key('nodes-by-genid',...).
<xsl:key name="nodes-by-genid" match="/ | node() | @*" use="generate-id ()"/>
Namespace nodes cannot be matched by<xsl:key>. Therefore we represent its parent and its name. Both parts are seperated by colon character: <id><gen><xsl:value-of select="generate-id(..)"/> </gen>:<pre><xsl:value-of select="name()"/></pre></id>
The string value of this representation allows for duplicate node elimination by key('no-duplicates',...) and Muenchian grouping.
<xsl:key name="no-duplicates" match="id" use="concat(gen/text(),substring (':',1+not(pre)),pre/text())"/>
An id-node-set is just a node-set of id-nodes.
Dereferencing an id-node-set is done this way, even without exslt:node-set() function. As a side-effect applying the key function sorts the nodes into document order (*)!
<xsl:variable name="result-nodes" select=" key('nodes-by-genid',$ecids/id[not(pre)]/gen) | key('nodes-by-genid',$ecids/id[pre]/gen)/ namespace::*[concat(generate-id(..),':',name())=$ecids/id]"/>
(*) not for Webkit based browsers Chrome and Safari.
id-node-sets can just be copied. Because of the contained id-value the copies still reference the same nodes as before.
While handling of a constant number of "pointers" could be achieved by <xsl:variable name="..." select="..."> there is no concept of set of references in XSLT, only a node-set. But passing node-set while calling a template would need to generate copies and cannot be used for representing pointers by itself.
All what is needed for XSLT pointers is XSLT 1.0 plus the exslt:node-set() function. This has the advantage that XSLT pointers work on the following browsers: Chrome, Firefox(**), Internet Explorer(***), Opera, Safari.
(**) Firefox does not support the namespace:: axis. (***) Support for Internet Explorer is provided by: <!-- from http://dpcarlisle.blogspot.com/2007/05/exslt-node-set-function.html --> <msxsl:script language="JScript" implements-prefix="exslt"> this['node-set'] = function (x) { return x; } </msxsl:script>
id-node-sets can preferably be used to implement eg. a location step as part of dyn:evaluate() function within XSLT itself along the "words": A location step identifies a new node-set relative to the context node-set. The location step is evaluated against each node in the context node-set, and the union of the resulting node-sets becomes the context node-set for the next step.
Find below a sequence of above operations with generated output. ...
(B) <!-- XSLT pointers, v1.0, 9/21/2010 --> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:exslt="http://exslt.org/common" xmlns:msxsl="urn:schemas-microsoft-com:xslt" exclude-result-prefixes="exslt msxsl" <xsl:output method="html"/>
<xsl:include href="serialize.xsl"/>
<!-- for id-node-set dereferencing --> <xsl:key name="nodes-by-genid" match="/ | node() | @*" use="generate-id()"/>
<!-- for id-node-set duplicate node elimination --> <xsl:key name="no-duplicates" match="id" use="concat(gen/text(),substring(':',1+not(pre)),pre/text())"/>
<!-- duplicate node elimination --> <xsl:template name="eliminate-duplicates"> <xsl:param name="ids"/> <xsl:for-each select="$ids"> <xsl:for-each select="id"> <xsl:if test="generate-id()=generate-id(key('no-duplicates',.)[1])"> <xsl:copy-of select="."/> </xsl:if> </xsl:for-each> </xsl:for-each> </xsl:template>
<!-- create id-node-set referencing all nodes from $nodes --> <xsl:template name="ids-by-nodes"> <xsl:param name="nodes"/> <xsl:for-each select="$nodes"> <xsl:choose> <xsl:when test="count(.|../namespace::*)=count(../namespace::*)"> <id><gen><xsl:value-of select="generate-id(..)"/> </gen>:<pre><xsl:value-of select="name()"/></pre></id> </xsl:when> <xsl:otherwise> <id><gen><xsl:value-of select="generate-id(.)"/></gen></id> </xsl:otherwise> </xsl:choose> </xsl:for-each> </xsl:template>
<xsl:template match="/">
<!-- sample id-node-set --> <xsl:variable name="ids"> <xsl:call-template name="ids-by-nodes"> <xsl:with-param name="nodes" select=" /comment() | /e1/@att | processing-instruction() | / | /e1/e2/e4/namespace::xml | /e1/e2 | /e1/text() | /e1/e2/*[local-name()='e3']/namespace::*[name()=''] | /e1/e2/namespace::m | /e1/e2/e4/namespace::ns | /e1/e2/namespace::ns"/> </xsl:call-template> </xsl:variable> <xsl:variable name="eids" select="exslt:node-set($ids)"/>
<!-- intentionally create duplicates --> <xsl:variable name="dids"> <xsl:copy-of select="$eids"/> <xsl:copy-of select="$eids"/> </xsl:variable> <xsl:variable name="edids" select="exslt:node-set($dids)"/>
<!-- eliminate duplicates --> <xsl:variable name="ndids"> <xsl:call-template name="eliminate-duplicates"> <xsl:with-param name="ids" select="$edids"/> </xsl:call-template> </xsl:variable> <xsl:variable name="endids" select="exslt:node-set($ndids)"/>
<!-- copy id-node-set --> <xsl:variable name="cids"> <xsl:for-each select="$endids/id"> <xsl:sort order="descending"/> <xsl:copy-of select="."/> </xsl:for-each> </xsl:variable> <xsl:variable name="ecids" select="exslt:node-set($cids)"/>
<!-- dereference id-node-set --> <xsl:variable name="result-nodes" select=" key('nodes-by-genid',$ecids/id[not(pre)]/gen) | key('nodes-by-genid',$ecids/id[pre]/gen)/ namespace::*[concat(generate-id(..),':',name())=$ecids/id]"/>
<html> <body>
<h3>XSLT pointers</h3>
<pre> (Referencing) Every non-namespace node will be represented by: <id><gen><xsl:value-of select="generate-id()"/> </gen></id> This allows to dereference the pointers by key('nodes-by-genid',...). <br/> <xsl:for-each select="document('')/xsl:stylesheet/xsl:key [@name='nodes-by-genid']"> <xsl:call-template name="doOutput"/> </xsl:for-each> <p/> Namespace nodes cannot be matched by<xsl:key>. Therefore we represent its parent and its name. Both parts are seperated by colon character: <id><gen><xsl:value-of select="generate-id(..)"/> </gen>:<pre><xsl:value-of select="name()"/> </pre></id>
The string value of this representation allows for duplicate node elimination by key('no-duplicates',...) and Muenchian grouping. <br/> <xsl:for-each select="document('')/xsl:stylesheet/xsl:key [@name='no-duplicates']"> <xsl:call-template name="doOutput"/> </xsl:for-each> <p/> An id-node-set is just a node-set of id-nodes. <br/> Dereferencing an id-node-set is done this way, even without exslt:node-set() function. As a side-effect applying the key function sorts the nodes into document order (*)! <br/> <xsl:for-each select="document ('')/xsl:stylesheet/xsl:template/xsl:variable[@name='result-nodes']"> <xsl:call-template name="doOutput"/> </xsl:for-each> <p/> (*) not for Webkit based browsers Chrome and Safari.
id-node-sets can just be copied. Because of the contained id-value the copies still reference the same nodes as before.
While handling of a constant number of "pointers" could be achieved by <xsl:variable name="..." select="..."> there is no concept of set of references in XSLT, only a node-set. But passing node-set while calling a template would need to generate copies and cannot be used for representing pointers by itself.
All what is needed for XSLT pointers is XSLT 1.0 plus the exslt:node-set() function. This has the advantage that XSLT pointers work on the following browsers: Chrome, Firefox(**), Internet Explorer(***), Opera, Safari.
(**) Firefox does not support the namespace:: axis. (***) Support for Internet Explorer is provided by: <xsl:for-each select="document('')/xsl:stylesheet/comment()[last()]"> <xsl:call-template name="doOutput"/> </xsl:for-each> <br/> <xsl:for-each select="document('')/xsl:stylesheet/msxsl:script"> <xsl:call-template name="doOutput"/> </xsl:for-each>
id-node-sets can preferably be used to implement eg. a location step as part of dyn:evaluate() function within XSLT itself along the "words": A location step identifies a new node-set relative to the context node-set. The location step is evaluated against each node in the context node-set, and the union of the resulting node-sets becomes the context node-set for the next step.
Find below a sequence of above operations with generated output. </pre>
<h4>seventypes.xml (input)</h4> <pre> <xsl:for-each select="/"> <xsl:call-template name="doOutput"/> </xsl:for-each> </pre>
<h4>determine id-node-set from node-set (reference)</h4> <pre> <xsl:for-each select="document('')/xsl:stylesheet/xsl:template [@match='/']/xsl:variable[@name='ids']"> <xsl:call-template name="doOutput"/> </xsl:for-each> <br/> <xsl:for-each select="document('')/xsl:stylesheet/xsl:template [@match='/']/xsl:variable[@name='eids']"> <xsl:call-template name="doOutput"/> </xsl:for-each> <p/> <xsl:for-each select="$eids/id"> <xsl:call-template name="doOutput"/> <xsl:text> </xsl:text> </xsl:for-each> </pre>
<h4>intentionally generate id-node-set with duplicates</h4> <pre> <xsl:for-each select="document('')/xsl:stylesheet/xsl:template [@match='/']/xsl:variable[@name='dids']"> <xsl:call-template name="doOutput"/> </xsl:for-each> <br/> <xsl:for-each select="document('')/xsl:stylesheet/xsl:template [@match='/']/xsl:variable[@name='edids']"> <xsl:call-template name="doOutput"/> </xsl:for-each> <br/><br/> <xsl:for-each select="$edids/id"> <xsl:call-template name="doOutput"/> <xsl:text> </xsl:text> </xsl:for-each> </pre>
<h4>eliminate duplicates in id-node-set</h4> <pre> <xsl:for-each select="document('')/xsl:stylesheet/xsl:template [@match='/']/xsl:variable[@name='ndids']"> <xsl:call-template name="doOutput"/> </xsl:for-each> <br/> <xsl:for-each select="document('')/xsl:stylesheet/xsl:template [@match='/']/xsl:variable[@name='endids']"> <xsl:call-template name="doOutput"/> </xsl:for-each> <br/><br/> <xsl:for-each select="$endids/id"> <xsl:call-template name="doOutput"/> <xsl:text> </xsl:text> </xsl:for-each> </pre>
<h4>copy of id-node-set (different order)</h4> <pre> <xsl:for-each select="document('')/xsl:stylesheet/xsl:template [@match='/']/xsl:variable[@name='cids']"> <xsl:call-template name="doOutput"/> </xsl:for-each> <br/> <xsl:for-each select="document('')/xsl:stylesheet/xsl:template [@match='/']/xsl:variable[@name='ecids']"> <xsl:call-template name="doOutput"/> </xsl:for-each> <p/> <xsl:for-each select="$ecids/id"> <xsl:call-template name="doOutput"/> <xsl:text> </xsl:text> </xsl:for-each> </pre>
<h4>determine node-set from id-node-set (dereference, document order (****))</h4> <pre> <xsl:for-each select="document('')/xsl:stylesheet/xsl:template [@match='/']//xsl:variable[@name='result-nodes']"> <xsl:call-template name="doOutput"/> </xsl:for-each>
(****) not for Webkit based browsers Chrome and Safari </pre>
<h4>output of $result-nodes</h4> <table border="1"> <xsl:for-each select="$result-nodes"> <tr> <td> <xsl:value-of select="position()"/> </td> <td> <xsl:text>/</xsl:text> <xsl:for-each select="ancestor::*"> <xsl:value-of select="name()"/> <xsl:text>/</xsl:text> </xsl:for-each> <br/><xsl:text> </xsl:text> <xsl:call-template name="doOutput"/> </td> </tr> </xsl:for-each>
</table> </body> </html> </xsl:template>
<!-- from http://dpcarlisle.blogspot.com/2007/05/exslt-node-set-function.html --> <msxsl:script language="JScript" implements-prefix="exslt"> this['node-set'] = function (x) { return x; } </msxsl:script>
</xsl:stylesheet>
Mit besten Gruessen / Best wishes,
Hermann Stamm-Wilbrandt Developer, XML Compiler, L3 Fixpack team lead WebSphere DataPower SOA Appliances ---------------------------------------------------------------------- IBM Deutschland Research& Development GmbH Vorsitzender des Aufsichtsrats: Martin Jetter Geschaeftsfuehrung: Dirk Wittkopp Sitz der Gesellschaft: Boeblingen Registergericht: Amtsgericht Stuttgart, HRB 243294
Current Thread |
---|
|
<- Previous | Index | Next -> |
---|---|---|
[xsl] XSLT pointers / questions, Hermann Stamm-Wilbra | Thread | [xsl] Making nested elements from e, Russell Urquhart |
[xsl] XSLT pointers / questions, Hermann Stamm-Wilbra | Date | Re: [xsl] Fwd: NLM3.0, Wendell Piez |
Month |