How to concatenate two node-sets such that order is respected?

喜你入骨 提交于 2019-12-13 13:25:42

问题


My understanding has been that, despite the fact that XSLT's "node-sets" are called "sets", they are, in fact, ordered lists of nodes (which is why each node is associated with an index). I've therefore been trying to use the "|" operator to concatenate node-sets such that the order of the nodes is respected.

What I am attempting to accomplish is something like the following JavaScript code:

[o1,o2,o3].concat([o4,o5,o6])

Which yields:

[o1,o2,o3,o4,o5,o6]

But, consider the following reduced example:

testFlatten.xsl

<?xml version="1.0"?>
<xsl:stylesheet 
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
 version="1.0">
 <xsl:output method="xml"/>

 <xsl:template match="/">

  <xsl:variable name="parentTransition" select="//*[@id='parentTransition']"/>
  <xsl:variable name="childTransition" select="//*[@id='childTransition']"/>
  <xsl:variable name="parentThenChildTransitions" select="$parentTransition | $childTransition"/>
  <xsl:variable name="childThenParentTransitions" select="$childTransition | $parentTransition"/>

  <return>
   <parentThenChildTransitions>
    <xsl:copy-of select="$parentThenChildTransitions"/>
   </parentThenChildTransitions>
   <childThenParentTransitions>
    <xsl:copy-of select="$childThenParentTransitions"/>
   </childThenParentTransitions>
  </return>

 </xsl:template>

</xsl:stylesheet>

Given the following input:

<?xml version="1.0"?>
<root>
        <element id="parentTransition"/>

 <element id="childTransition"/>
</root>

Which yields (with xsltproc):

<?xml version="1.0"?>
<return>
    <parentThenChildTransitions>
        <element id="parentTransition"/><element id="childTransition"/>
    </parentThenChildTransitions>
    <childThenParentTransitions>
        <element id="parentTransition"/><element id="childTransition"/>
    </childThenParentTransitions>
</return>

So the "|" operator in fact does not respect the order of the node-set operands. Is there a way I can concatenate node-sets such that order is respected?


回答1:


This is actually not an XSLT but an XPath question.

In XPath 1.0 there isn't anything similar to a "list" datatype. A node-set is a set and it has no order.

In XPath 2.0 there is the sequence data type. Any items in a sequence are ordered. This has nothing to do with document order. Also, the same item (or node) can appear more than once in a sequence.

So, in XSLT 2.0 one just uses the XPath 2.0 sequence concatenation operator ,:

//*[@id='parentTransition'] , //*[@id='childTransition']

and this evaluates to the sequence of all elements in the document with id attribute 'parentTransition' followed by all elements in the document with id attribute 'childTransition'

In XSLT it is still possible to access and process nodes not in document order: for example using the <xsl:sort> instruction -- however the set of nodes that are processed as result of <xsl:apply-templates> or <xsl:for-each> is a node-list -- not a node-set.

Another example of evaluating nodes not in document order is the position() function within <xsl:apply-templates> or <xsl:for-each> that have a <xsl:sort> child or within a predicate of a location step (of an XPath expression) in which a reverse axis is used (such as ancesstor:: or preceeding::)




回答2:


In XSLT 1.0, you can process nodes in a selected order (for example by use of xsl:sort), but you can't hold a list of nodes in a variable. The only thing you can hold in a variable (or pass to a template, etc) is a node-set; node-sets have no intrinsic order, but when you process them, they are always processed in document order unless you use xsl:sort to request a different processing order.

You might be able to solve your problem by copying the nodes:

<xsl:variable name="temp">
  <xsl:copy-of select="$ns0"/>
  <xsl:copy-of select="$ns1"/>
</xsl:variable>
...
<xsl:apply-templates select="exslt:node-set($temp/*)"/>

but this depends on your use-case.

Switch to XSLT 2.0 if you can!




回答3:


The "|" operator will retain nodes in document order. In XSLT 1.0 you will need to have sequential copy or for-each operations.

<xsl:copy-of select="$parentTransition"/>
<xsl:copy-of select="$childTransition"/>


来源:https://stackoverflow.com/questions/4610921/how-to-concatenate-two-node-sets-such-that-order-is-respected

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!