XSL中的多元素/属性排序

时间:2009-06-15 20:25:23

标签: xml xslt

我正在尝试使用XSL文档对几个级别的数据进行排序。我得到了它的工作,但它使用了一堆递归。我试图避免这种情况,因为我认为使用单个节点的多种排序已经有效。有没有办法将下面的XSL更改为更少的递归?我在USER / USERID上排序,然后是ROLE / @ name,最后是ACTIONINFO / @ actionfrom

工作样式表:     
             

  <xsl:template match="/">
    <xsl:apply-templates select="USERACTIONINFO"/>
  </xsl:template>

  <xsl:template match="USERACTIONINFO">
    <xsl:copy>
      <xsl:for-each select="USER">
        <xsl:sort select="USERID"/>
        <xsl:copy>
          <xsl:copy-of select="USERID" />
          <xsl:for-each select="ROLE">
            <xsl:sort select="@name"/>
            <xsl:copy>
              <xsl:copy-of select="@name" />
              <xsl:apply-templates select="ACTIONINFO">
                <xsl:sort select="@actionfrom"/>
              </xsl:apply-templates>
            </xsl:copy>
          </xsl:for-each>
        </xsl:copy>
      </xsl:for-each>
    </xsl:copy>
  </xsl:template>

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

要测试的一些XML:

 <USERACTIONINFO >
  <USER>
    <USERID>SSSUSER</USERID>
    <ROLE name="ZZ_UPD">
      <ACTIONINFO  actionfrom="ZC"  />
      <ACTIONINFO  actionfrom="AC"  />
    </ROLE>
    <ROLE name="QQ_UPD">
      <ACTIONINFO  actionfrom="AZCC11"  />
      <ACTIONINFO  actionfrom="ACC11"  />
    </ROLE>
  </USER>
  <USER>
    <USERID>AAAUSER</USERID>
    <ROLE name="PP_UPD">
      <ACTIONINFO actionfrom="ZZADBENF"  />
    </ROLE>
    <ROLE name="PP_BOEE">
      <ACTIONINFO actionfrom="BOM02"  />
    </ROLE>
    <ROLE name="PP_SS">
      <ACTIONINFO actionfrom="AZDBENF"  />
      <ACTIONINFO actionfrom="ADDBEN" />
    </ROLE>
  </USER>
</USERACTIONINFO>

实际正确输出:

   <USERACTIONINFO>
  <USER>
    <USERID>AAAUSER</USERID>
    <ROLE name="PP_BOEE">
      <ACTIONINFO actionfrom="BOM02" />
    </ROLE>
    <ROLE name="PP_SS">
      <ACTIONINFO actionfrom="ADDBEN" />
      <ACTIONINFO actionfrom="AZDBENF" />
    </ROLE>
    <ROLE name="PP_UPD">
      <ACTIONINFO actionfrom="ZZADBENF" />
    </ROLE>
  </USER>
  <USER>
    <USERID>SSSUSER</USERID>
    <ROLE name="QQ_UPD">
      <ACTIONINFO actionfrom="ACC11" />
      <ACTIONINFO actionfrom="AZCC11" />
    </ROLE>
    <ROLE name="ZZ_UPD">
      <ACTIONINFO actionfrom="AC" />
      <ACTIONINFO actionfrom="ZC" />
    </ROLE>
  </USER>
</USERACTIONINFO>

我认为我应该能够做但却无法工作:

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

    <xsl:template match="/">
    <xsl:apply-templates select="USERACTIONINFO"/>
  </xsl:template>

  <xsl:template match="USERACTIONINFO">
    <xsl:copy>
      <xsl:apply-templates select="USER">
        <xsl:sort select="USERID"/>
        <xsl:sort select="ROLE/@name"/>
        <xsl:sort select="ROLE/ACTIONINFO/@actionfrom"/>
      </xsl:apply-templates>
    </xsl:copy>
  </xsl:template>

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

2 个答案:

答案 0 :(得分:2)

我不确定你为什么要这么努力。 <xsl:apply-templates>为您完成所有必要的迭代。您根本不使用(或需要)任何递归。只是不要使用嵌套<xsl:for-each>三层深度的东西来对付它。

单独的专用模板更易于阅读和维护:

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

  <!-- in USERACTIONINFO: output USER sorted by USERID -->
  <xsl:template match="USERACTIONINFO">
    <xsl:copy>
      <xsl:apply-templates select="USER">
        <xsl:sort select="USERID" />
      </xsl:apply-templates>
    </xsl:copy>
  </xsl:template>

  <!-- in USER: output ROLE sorted by @name -->
  <xsl:template match="USER">
    <xsl:copy>
      <xsl:copy-of select="USERID" />
      <xsl:apply-templates select="ROLE">
        <xsl:sort select="@name" />
      </xsl:apply-templates>
    </xsl:copy>
  </xsl:template>

  <!-- in ROLE: output ACTIONINFO sorted by @actionfrom -->
  <xsl:template match="ROLE">
    <xsl:copy>
      <xsl:copy-of select="@*" />
      <xsl:apply-templates select="ACTIONINFO">
        <xsl:sort select="@actionfrom" />
      </xsl:apply-templates>
    </xsl:copy>
  </xsl:template>

  <!-- output ACTIONINFO verbatim -->
  <xsl:template match="ACTIONINFO">
    <xsl:copy-of select="." />
  </xsl:template>

</xsl:stylesheet>

结果符合要求:

<USERACTIONINFO>
  <USER>
    <USERID>AAAUSER</USERID>
    <ROLE name="PP_BOEE">
      <ACTIONINFO actionfrom="BOM02" />
    </ROLE>
    <ROLE name="PP_SS">
      <ACTIONINFO actionfrom="ADDBEN" />
      <ACTIONINFO actionfrom="AZDBENF" />
    </ROLE>
    <ROLE name="PP_UPD">
      <ACTIONINFO actionfrom="ZZADBENF" />
    </ROLE>
  </USER>
  <USER>
    <USERID>SSSUSER</USERID>
    <ROLE name="QQ_UPD">
      <ACTIONINFO actionfrom="ACC11" />
      <ACTIONINFO actionfrom="AZCC11" />
    </ROLE>
    <ROLE name="ZZ_UPD">
      <ACTIONINFO actionfrom="AC" />
      <ACTIONINFO actionfrom="ZC" />
    </ROLE>
  </USER>
</USERACTIONINFO>

答案 1 :(得分:0)

不,你不能。

你意识到你的代码

<xsl:apply-templates select="USER">
  <xsl:sort select="USERID"/>
  <xsl:sort select="ROLE/@name"/>
</xsl:apply-templates>

产生了负面意义,因为它建议您按照角色名称对用户ID进行排序?

说真的,当你在USER上应用模板时,你会对用户进行排序,而不是对用户进行排序。如果它采取其他行动,那就太棒了。

所以不,你应该坚持完全合法的第一个例子。

您可以将其分解为几个模板,从而使其更长,但不那么深,等等。