xsl for-each to create an iterable node list

2019-08-02 07:18发布

I need to iterate over the elements of an XML file in sorted order of their language field many times. What I try is to get an iterable list of the languages as follows:

<xsl:variable name="languages">
  <xsl:for-each select="elem/FIELD[@NAME='language']">
    <xsl:sort select="."/>
    <xsl:value-of select="."/>
  </xsl:for-each>
</xsl:variable>

While I can verify with a

<xsl:value-of select="$languages"/>

that the sorting works, I cannot iterate like

<xsl:for-each select="$langauges">...</xsl:for-each>

because the XSL processor complains that select expression does not evaluate to a node set.

Edit: Not sure whether this is important, but I have

<xsl:output encoding="UTF-8" 
          method="xml" 
          media-type="text/xml" 
          indent="yes" />

What do I have to insert in the loop to make the result into a node set? Is this at all possible?

标签: xslt
4条回答
一纸荒年 Trace。
2楼-- · 2019-08-02 07:54

Given you say that

XSL processor complains that select expression does not evaluate to a node set.

I assume you're using XSLT 1.0 rather than 2.0. In XSLT 1.0 when you declare a variable with content rather than a select attribute, the resulting variable contains something called a "result tree fragment" rather than a node set. You can apply value-of and copy-of to a RTF to send it to the output but you can't navigate into it using XPath expressions.

Most XSLT processors provide some sort of extension function to convert a RTF into a real node set - msxsl for the Microsoft processor or exslt for most others.

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"
                xmlns:exsl="http://exslt.org/common"
                exclude-result-prefixes="exsl">

  <!-- .... -->

  <xsl:variable name="languagesRTF">
    <xsl:for-each select="elem/FIELD[@NAME='language']">
      <xsl:sort select="."/>
      <lang><xsl:value-of select="."/></lang>
    </xsl:for-each>
  </xsl:variable>

  <xsl:variable name="languages" select="exsl:node-set($languagesRTF)/lang" />

In XSLT 2.0 there is no distinction between result tree fragments and node sets - they're both treated as sequences - so you don't need the extension function in that version.

查看更多
霸刀☆藐视天下
3楼-- · 2019-08-02 07:54

You need to convert the result tree fragment in your variable into a node-set, using the EXSLT node-set() function.

查看更多
迷人小祖宗
4楼-- · 2019-08-02 07:54

XSLT 1.0 allows you to process a node-set in sorted order, but it doesn't allow you to save a sorted sequence in a variable (the data model only has sets, not sequences). The only way you can save sorted data in 1.0 is to construct a new tree containing copies of the original elements in a different order, and then use the node-set() extension to make this tree processable.

This changes in XSLT 2.0, which has a data model based on sequences. In 2.0 you can save a sorted sequence of nodes in a variable without copying the nodes into a new tree.

查看更多
\"骚年 ilove
5楼-- · 2019-08-02 08:02

Try the following:

To declare variable:

<xsl:variable name="languages">
 <xsl:for-each select="elem/FIELD[@NAME='language']">
  <xsl:sort select="."/>
  <xsl:copy-of select="."/>
 </xsl:for-each>
</xsl:variable>

And to loop:

<xsl:for-each select="$languages/*">
查看更多
登录 后发表回答