xsl-list
[Top] [All Lists]

Re: [xsl] Sort XML based on Tokenized String of sort by fields

2008-05-28 09:46:46
I think following idea might work:

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

<xsl:output method="xml" indent="yes" />

<xsl:template match="REPORT">
  <xsl:variable name="x" select="REPORT_FORMAT/PARENT_NODE" />
  <xsl:variable name="y" select="REPORT_FORMAT/CHILD_NODE" />
  <xsl:variable name="order-by" select="for $i in
tokenize(REPORT_FORMAT/ORDER_BY,',') return normalize-space($i)" />
  <result>
    <xsl:for-each select="*[local-name() = $x]/*[local-name() = $y]">
      <xsl:sort select="string-join(for $j in $order-by return
*[local-name() = $j],':')" />
      <xsl:copy-of select="." />
    </xsl:for-each>
  </result>
</xsl:template>

</xsl:stylesheet>

You must ensure that in this part of the stylesheet .......
*[local-name() = $j],':')" /> the string (here a colon) should be
unusual and does not exist in the data.

I have done limited testing of this stylesheet, and it seems to work fine.

On Thu, May 22, 2008 at 4:50 AM, Rebecca Sapir
<rsapir(_at_)merlinsecurities(_dot_)com> wrote:
I am also using the SAXON parser.
XSLT version 2

I am trying to sort by a tokenized string. I get an ORDER_BY string with 
field names to sort by which are separated by commas.

The number of fields in the order_by string names can vary!!!

The X and X_ROW names are not constant. They can vary and are therefore 
passed in the PARENT_NODE and CHILD_NODE fields.
For example:

The XML I get is formatted as

<REPORT>
     <REPORT_HDR>
     ...
     </REPORT_HDR>
           <REPORT_FORMAT>
                       <ORDER_BY> DATE, ID_1, ID_2, TYPE</ORDER_BY>
                       <PARENT_NODE>X</PARENT_NODE>
                       <CHILD_NODE>X_ROW</CHILD_NODE>
           </REPORT_FORMAT>
           <X>
               <X_ROW>
                             <DATE></DATE>
                             <ID_1></ID_1>
                             <TYPE ></TYPE>
                             ...
                   </X_ROW>
               <X_ROW>
                             <DATE></DATE>
                             <ID_1></ID_1>
                             <TYPE ></TYPE>
                             ...
                   </X_ROW>
               <X_ROW>
                             <DATE></DATE>
                             <ID_1></ID_1>
                             <TYPE ></TYPE>
                             ...
                   </X_ROW>
               <X_ROW>
                             <DATE></DATE>
                             <ID_1></ID_1>
                             <TYPE ></TYPE>
                             ...
                   </X_ROW>
</REPORT>

In this case I want to sort the X/X_ROW's by DATE then ID_1 then ID_2 etc

This is one solution I have so far...
(The other one is below which uses recursion instead of tokenize)

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"; 
version="2.0" xmlns:saxon="http://saxon.sf.net/";>
<xsl:variable name="PATH" select="REPORT/REPORT_FORMAT/PARENT_NODE" />
<xsl:variable name="CHILD" select="REPORT/REPORT_FORMAT/CHILD_NODE" />
<xsl:variable name="ORDER_BY" select="REPORT/REPORT_FORMAT/ORDER_BY"/>
<xsl:variable name="ORDER_BY_TOKEN" select="tokenize($ORDER_BY,',')"/>

<xsl:template match="REPORT/*">

<xsl:choose>

 <xsl:when test="local-name() = $PATH">

 <xsl:copy>

   <xsl:for-each select="*">
    <xsl:sort select="saxon:evaluate(normalize-space($ORDER_BY_TOKEN[1]))"/>
    <xsl:sort select="saxon:evaluate(normalize-space($ORDER_BY_TOKEN[2]))"/>
    <xsl:sort select="saxon:evaluate(normalize-space($ORDER_BY_TOKEN[3]))"/>
    <xsl:sort select="saxon:evaluate(normalize-space($ORDER_BY_TOKEN[4]))"/>
    <xsl:copy-of select="."/>
   </xsl:for-each>

 </xsl:copy>

 </xsl:when>

 <xsl:otherwise>

 <xsl:copy-of select="."/>

 </xsl:otherwise>

</xsl:choose>

</xsl:template>

<xsl:template match="*">

<xsl:copy>
 <xsl:apply-templates/>
</xsl:copy>

</xsl:template>

</xsl:stylesheet>

My issue of course is that I am hard coding the ORDER_BY_TOKEN item that I 
want. In truth there are a variable number of fields that can get passed to 
me.

Ideally if I could surround the sorts with a for loop that would be what I am 
looking for. But this does not work:
 <xsl:for-each select="*">

   <xsl:for-each select="1 to count($ORDER_BY_TOKEN)">
     <xsl:sort select="saxon:evaluate(normalize-space($ORDER_BY_TOKEN[.]))"/>
     <xsl:copy-of select="."/>
   </xsl:for-each>

 </xsl:for-each>


Instead of tokenizing I tried a recursive function. This will perform the 
sorts but not consecutively. So in effect the last sort is really the only 
one that gets applied. Ie. it does not have the effect of sort by Date then 
id_1 then id_2 then...

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"; 
version="2.0" xmlns:saxon="http://saxon.sf.net/";>

<xsl:variable name="PATH" select="REPORT/REPORT_FORMAT/PARENT_NODE" />
<xsl:variable name="ORDER_BY" select="REPORT/REPORT_FORMAT/ORDER_BY"/>


<xsl:template match="REPORT/*">

<xsl:choose>

<xsl:when test="local-name() = $PATH">

 <xsl:copy>

   <xsl:call-template name="tokenize">
     <xsl:with-param name="string" select="$ORDER_BY" />
   </xsl:call-template>

 </xsl:copy>

 </xsl:when>

 <xsl:otherwise>
  <xsl:copy-of select="."/>
 </xsl:otherwise>

</xsl:choose>
</xsl:template>

<xsl:template match="*">
 <xsl:copy>
      <xsl:apply-templates/>
 </xsl:copy>
</xsl:template>

<xsl:template name="tokenize">

 <xsl:param name="string" />

 <xsl:choose>

 <xsl:when test="contains($string, ',')">

  <xsl:apply-templates>
   <xsl:sort 
select="saxon:evaluate(normalize-space(substring-before($string,',')))"/>
  </xsl:apply-templates>

 <xsl:call-template name="tokenize">
   <xsl:with-param name="string" select="substring-after($string, ',')" />
 </xsl:call-template>

 </xsl:when>

 <xsl:otherwise>
 <xsl:copy>

  <xsl:apply-templates>
   <xsl:sort select="saxon:evaluate(normalize-space($string))"/>
  </xsl:apply-templates>

 <xsl:copy-of select="."/>
 </xsl:copy>
 </xsl:otherwise>

</xsl:choose>

</xsl:template>

</xsl:stylesheet>

Any help would be greatly appreciated.

Thanks.


-- 
Regards,
Mukul Gandhi

--~------------------------------------------------------------------
XSL-List info and archive:  http://www.mulberrytech.com/xsl/xsl-list
To unsubscribe, go to: http://lists.mulberrytech.com/xsl-list/
or e-mail: <mailto:xsl-list-unsubscribe(_at_)lists(_dot_)mulberrytech(_dot_)com>
--~--