如何更改每个xsl的顺序?

时间:2010-08-04 18:04:15

标签: xslt

只有在满足某些条件时,我才需要更改执行for-each的顺序。

以下是我的XML:

<OptionList>
    <Option name="My First Option" />
    <Option name="My Second Option" />
</OptionList>

但是,在某些情况下,我的XML可以是这样的:

<OptionList>
    <Option />
    <Option name="My Second Option" />
</OptionList>

在我的XSL中,我正在做一个像这样的人:

<xsl:for-each select="//OptionList/Option">
    {...}
</xsl:for-each>

我知道我可以在for-each:

中使用此行更改Option节点的顺序
<xsl:sort select="position()" data-type="number" order="descending" />

问题是我希望我的订单只在第一个Option节点为空且没有name属性时才下降。否则,我想保留默认的升序。

关于如何实现这一点的任何意见?到目前为止,我尝试的所有内容都以“变量超出范围”或无效使用xpath函数结束。

3 个答案:

答案 0 :(得分:5)

您可以使用hack根据条件更改排序顺序:

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output method="xml" indent="yes"/>

  <xsl:template match="/">
    <result>
    <xsl:for-each select="//OptionList/Option">
      <xsl:sort data-type="number" order="ascending"
        select="position()*(-2*number(not(//OptionList/Option[1]/@name))+1)"/>

      <option>
        <xsl:value-of select="@name"/>
      </option>
    </xsl:for-each>
    </result>

  </xsl:template>
</xsl:stylesheet>

黑客是number((true())返回1而number(false())返回0.因此,表达式

-2 * number(not(//OptionList/Option[1]/@name)) + 1
如果第一个1元素具有option属性,则

评估为name,否则评估为-1。这用作反转排序顺序的因素。

答案 1 :(得分:1)

可以使用<xsl:sort>轻松指定生成所需输出的顺序:

这很自然,很简单,几乎没有任何黑客攻击。

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>
 <xsl:strip-space elements="*"/>

 <xsl:variable name="vOrder" select=
   "2*boolean(/*/Option[1]/@name)-1"/>

 <xsl:template match="node()|@*">
  <xsl:copy>
   <xsl:apply-templates select="node()|@*"/>
  </xsl:copy>
 </xsl:template>

 <xsl:template match="/*">
   <xsl:copy>
     <xsl:for-each select="Option">
       <xsl:sort data-type="number" select="$vOrder* position()"/>

       <xsl:apply-templates select="."/>
     </xsl:for-each>
   </xsl:copy>
 </xsl:template>
</xsl:stylesheet>

对此XML文档执行此转换时

<OptionList>
    <Option name="My First Option" />
    <Option name="My Second Option" />
    <Option name="My Third Option" />
</OptionList>

产生了想要的正确结果

<OptionList>
   <Option name="My First Option"/>
   <Option name="My Second Option"/>
   <Option name="My Third Option"/>
</OptionList>

现在对此XML文档执行相同的转换

<OptionList>
    <Option />
    <Option name="My Second Option" />
    <Option name="My Third Option" />
</OptionList>

再次产生想要的正确结果

<OptionList>
   <Option name="My Third Option"/>
   <Option name="My Second Option"/>
   <Option/>
</OptionList>

解释:变量$vOrder以这样的方式定义,如果第一个-1元素没有Option,则为name如果第一个+1元素具有Option属性,则属性为name。在这里,我们使用false()自动转换为0true()转换为1的事实。

我们还使用这样的事实:当正数(位置)增加的顺序中的每个数字的符号被反转时,新序列的顺序变得越来越小。

答案 2 :(得分:0)

检查第一个选项是否具有名称node

<xsl:if test="Option/[position()=1]/@name">sort here</xsl:test>