tags:

views:

393

answers:

2

Hello Again,

I am trying to sort several levels of data using a XSL document. I got it to work but it is using a bunch of recursion. I am trying to avoid this because I figured using multiple sorts withing a single node shold have worked. Is there a way to change the XSL below to be less recursive? I am sorting on USER/USERID, then ROLE/@name and finally ACTIONINFO/@actionfrom

Working Stylesheet:

  <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>

Some XML to test with:

 <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>

Actual CORRECT output:

   <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>

What I thought I should be able to do but didn't work:

<xsl:stylesheet version='1.0'
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"&gt;  
  <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>
A: 

No, you can't.

Do you realize that your code

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

made negative amounts of sense because it suggested that you're going to sort users with same userid by their role name?

Seriously, when you apply-templates over USER, you're sorting users, not what's inside them. It would be bizzare if it acted otherwise.

So no, you should stick with your perfectly legitimate first example.

You can break it into a few templates, thus making it somewhat longer but less deep, tho.

alamar
+1  A: 

I'm not sure why you make your life so hard. <xsl:apply-templates> does all the necessary iteration for you. You don't use (or need) any recursion for this task at all. Just don't work against it with things like nesting <xsl:for-each> three levels deep.

Separate, dedicated templates are easier to read and to maintain:

<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>

Result is as desired:

<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>
Tomalak
Tomalak, thanks again for your response. Do you have any recommendations for a good XSL book to use as a learning guide? Not looking for a novel but something that covers all the bases well with examples.
Jay
No, I don't, sorry. I've never read one myself.
Tomalak