Georgy Netmo Dorokhov
Georgy Netmo Dorokhov

Reputation: 23

XSLT: Sort based on child node's attribute doesn't work

I just can't get sort function to work.

Basically I'm trying to do same as XSLT sort by attribute value, but its just doesn't work

Input is

<?xml version="1.0" encoding="UTF-8"?>
<response>

<result>
  <doc>
    <str name="hash1">EBFF15C2FB15BDD9C069EDF272EF43E738B276AA</str>
    <str name="org_data">
<items>
<orgdata amount ="5433" />
</items>
</str>
</doc>
  <doc>
    <str name="hash1">8CB2237D0679CA88DB6464EAC60DA96345513964</str>
    <str name="org_data">
<items>
<orgdata amount_rur="300"/>
<orgdata amount_rur="100"/>
<orgdata amount_rur="200"/>
<orgdata amount_rur="200" />
</items>
</str>
 </doc>
</result>
</response>

And I'm trying something like this:

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

    <xsl:param name="rta-hashParticipantData1" select = "'8CB2237D0679CA88DB6464EAC60DA96345513964'"  />
    <xsl:param name="rta-hashParticipantData2"  select = "'EBFF15C2FB15BDD9C069EDF272EF43E738B276AA'" />
    <xsl:param name="rta-hashParticipantData3" />
    <xsl:param name="rta-role" select = "'123'"  />

    <xsl:template match="result">
        <xsl:copy>
            <roleName Role="{$rta-role}">
            <xsl:for-each-group select="doc" group-by="str[@name='hash1']">
                    <xsl:choose>
                        <xsl:when test="current-group()/str[@name='hash1']=$rta-hashParticipantData1">
                            <hashData hashName= '1' hashValue="{current-group()/str[@name='hash1']}" >
                                <xsl:apply-templates select="current-group()/str[@name='org_data']" />
                            </hashData>
                        </xsl:when>
                        <xsl:when test="current-group()/str[@name='hash1']=$rta-hashParticipantData2">
                            <hashData hashName= '2' hashValue="{current-group()/str[@name='hash1']}" >
                                <xsl:apply-templates select="current-group()/str[@name='org_data']" />
                            </hashData>
                        </xsl:when>
                        <xsl:when test="current-group()/str[@name='hash1']=$rta-hashParticipantData3">
                            <hashData hashName= '3' hashValue="{current-group()/str[@name='hash1']}" >
                                <xsl:apply-templates select="current-group()/str[@name='org_data']" />
                            </hashData>
                        </xsl:when>
                    </xsl:choose>
            </xsl:for-each-group>
            </roleName>
        </xsl:copy>
    </xsl:template>
    <xsl:template match="photoUrls|str">
        <xsl:apply-templates select="*"/>
    </xsl:template>
   <xsl:template match="@* | node()">
    <xsl:copy>
      <xsl:apply-templates select="@* | node()"/>
    </xsl:copy>
  </xsl:template>
 <xsl:template match="roleName">
    <xsl:copy>
      <xsl:apply-templates select="hashData">
        <xsl:sort select="@hashName"/>
      </xsl:apply-templates>
    </xsl:copy>
  </xsl:template>
</xsl:stylesheet>

So, I'm:

1) Grouping data by hash1 value

2) Adding some information (role, and hash number) based on global params. More specifically, I'm adding hashData element with hashName (hash number) and hashValue (hash value for debug) attributes.

3) Cleaning 'str' stuff

4) Trying to sort data within one role by hashData/@hashName

Last one doesn't work (it works if I'm doing it with another XSL tho). So, the question is - how to do it all in same XSL, and why it not working my way?

Upvotes: 0

Views: 84

Answers (2)

Tim C
Tim C

Reputation: 70618

One approach could be to define a single parameter, consisting of a comma-delimited list of hashes, and then make use of tokenize and index-of to work out the hashName.

This would have the advantage of removing the xsl:choose, and allowing more than 3 parameters too.

Try this XSLT

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

    <xsl:param name="rta-role" select = "'123'"  />

    <xsl:param name="rta-hashParticipantData" select="'8CB2237D0679CA88DB6464EAC60DA96345513964,EBFF15C2FB15BDD9C069EDF272EF43E738B276AA'" />

    <xsl:template match="result">
        <xsl:variable name="rta-hashParticipantDataList" select="tokenize($rta-hashParticipantData, ',')" />
        <xsl:copy>
            <roleName Role="{$rta-role}">
                <xsl:for-each-group select="doc" group-by="str[@name='hash1']">
                    <xsl:sort select="index-of($rta-hashParticipantDataList, current-grouping-key())" />
                    <hashData hashName="{index-of($rta-hashParticipantDataList, current-grouping-key())}" hashValue="{current-group()/str[@name='hash1']}" >
                        <xsl:apply-templates select="current-group()/str[@name='org_data']" />
                    </hashData>
                </xsl:for-each-group>
            </roleName>
        </xsl:copy>
    </xsl:template>

    <xsl:template match="photoUrls|str">
        <xsl:apply-templates select="*"/>
    </xsl:template>

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

</xsl:stylesheet>

Upvotes: 1

Martin Honnen
Martin Honnen

Reputation: 167516

It seems you want to sort the result you want to create so try to wrap your grouping creating the hashData elements into xsl:perform-sort:

<xsl:perform-sort>
  <xsl:sort select="@hashName"/>
  <xsl:for-each-group select="doc" group-by="str[@name='hash1']">
                    <xsl:choose>
                        <xsl:when test="current-group()/str[@name='hash1']=$rta-hashParticipantData1">
                            <hashData hashName= '1' hashValue="{current-group()/str[@name='hash1']}" >
                                <xsl:apply-templates select="current-group()/str[@name='org_data']" />
                            </hashData>
                        </xsl:when>
                        <xsl:when test="current-group()/str[@name='hash1']=$rta-hashParticipantData2">
                            <hashData hashName= '2' hashValue="{current-group()/str[@name='hash1']}" >
                                <xsl:apply-templates select="current-group()/str[@name='org_data']" />
                            </hashData>
                        </xsl:when>
                        <xsl:when test="current-group()/str[@name='hash1']=$rta-hashParticipantData3">
                            <hashData hashName= '3' hashValue="{current-group()/str[@name='hash1']}" >
                                <xsl:apply-templates select="current-group()/str[@name='org_data']" />
                            </hashData>
                        </xsl:when>
                    </xsl:choose>
   </xsl:for-each-group>
</xsl:perform-sort>

Or group into a variable and then push the contents of the variable through apply-templates. Your template with match="roleName" can only be applied if you have created elements of that name in an input tree or a temporary tree.

Upvotes: 1

Related Questions