Hello,
I was finally successful in getting XSLT pointers working.
After some problems in addressing namespace nodes there was
a solution for that. The most tricky thing is dereferencing
an id-node-set back to the node-set it represents.
Find below (A) an explanation on how XSLT pointers work.
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
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
[(_at_)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
[(_at_)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[(_at_)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
[(_at_)match='/']/xsl:variable[(_at_)name='ids']">
<xsl:call-template name="doOutput"/>
</xsl:for-each>
<br/>
<xsl:for-each select="document('')/xsl:stylesheet/xsl:template
[(_at_)match='/']/xsl:variable[(_at_)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
[(_at_)match='/']/xsl:variable[(_at_)name='dids']">
<xsl:call-template name="doOutput"/>
</xsl:for-each>
<br/>
<xsl:for-each select="document('')/xsl:stylesheet/xsl:template
[(_at_)match='/']/xsl:variable[(_at_)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
[(_at_)match='/']/xsl:variable[(_at_)name='ndids']">
<xsl:call-template name="doOutput"/>
</xsl:for-each>
<br/>
<xsl:for-each select="document('')/xsl:stylesheet/xsl:template
[(_at_)match='/']/xsl:variable[(_at_)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
[(_at_)match='/']/xsl:variable[(_at_)name='cids']">
<xsl:call-template name="doOutput"/>
</xsl:for-each>
<br/>
<xsl:for-each select="document('')/xsl:stylesheet/xsl:template
[(_at_)match='/']/xsl:variable[(_at_)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
[(_at_)match='/']//xsl:variable[(_at_)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
--~------------------------------------------------------------------
XSL-List info and archive: http://www.mulberrytech.com/xsl/xsl-list
To unsubscribe, go to: http://lists.mulberrytech.com/xsl-list/
or e-mail: <mailto:xsl-list-unsubscribe(_at_)lists(_dot_)mulberrytech(_dot_)com>
--~--