p2p.wrox.com Forums

p2p.wrox.com Forums (http://p2p.wrox.com/index.php)
-   XSLT (http://p2p.wrox.com/forumdisplay.php?f=86)
-   -   Position of sorted nodes (http://p2p.wrox.com/showthread.php?t=59785)

tall rog July 5th, 2007 02:42 AM

Position of sorted nodes
 
Hi,

I am using Saxon 8 on XP.

I would like to test the existence of ordered nodes and output either the value of an attribute on the ordered node or a constant value if the node does not exist.

For testing I have the following XML
<TEST>
    <FLD1 attr1='attr11' attr2='attr12' attr3='attr13'>
        <CMP num='11' />
        <CMP num='13' />
        <CMP num='12' />
        <CMP num='14' />
    </FLD1>

    <FLD1 attr1='attr24' attr2='attr25' attr3='attr26'>
        <CMP num='21' />
        <CMP num='23' />
        <CMP num='22' />
    </FLD1>

    <FLD1 attr1='attr04' attr2='attr05' attr3='attr06'>
        <CMP num='02' />
        <CMP num='01' />
    </FLD1>
</TEST>


So we have a number of FLD1 tags which have a number of CMP tags each with a 'num' attribute. There can be anything up to 5 CMP nodes for each FLD1 node

I need to convert this to a pipe delimited format as follows...
   attr11|attr12|attr13|11|12|13|14|NA|
   attr24|attr25|attr26|21|22|23|NA|NA|
   attr04|attr05|attr06|01|02|NA|NA|NA|

So if a Node does not exist then the field should contain 'NA'.

Note that the data from the CMP nodes are sorted by the 'num' attribute. There is no need to sort the FLD nodes.


The closest I have come is using the XSL below, but this does not order the CMP nodes that are there. What I think I need to do is sort the nodes and then test for their existence using the "displayCmp" template I have created. But I can see no way of doing that.

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

<xsl:template match="/TEST">

    <xsl:for-each select="./FLD1">

        <xsl:text>|</xsl:text>
        <xsl:value-of select="@attr1"/>
        <xsl:text>|</xsl:text>
        <xsl:value-of select="@attr2"/>
        <xsl:text>|</xsl:text>
        <xsl:value-of select="@attr3"/>
        <xsl:text>|</xsl:text>

                <xsl:call-template name="displayCmp">
                    <xsl:with-param name="component" select="'1'"/>
                </xsl:call-template>

                <xsl:call-template name="displayCmp">
                    <xsl:with-param name="component" select="'2'"/>
                </xsl:call-template>

                <xsl:call-template name="displayCmp">
                    <xsl:with-param name="component" select="'3'"/>
                </xsl:call-template>

                <xsl:call-template name="displayCmp">
                    <xsl:with-param name="component" select="'4'"/>
                </xsl:call-template>

                <xsl:call-template name="displayCmp">
                    <xsl:with-param name="component" select="'5'"/>
                </xsl:call-template>

         <xsl:text>
</xsl:text>
    </xsl:for-each>

</xsl:template>


<xsl:template name="displayCmp">
        <xsl:param name="component"/>
    <xsl:choose>
        <xsl:when test="CMP[position()=$component]">
            <xsl:value-of select="CMP[position()=$component]/@num"/>
        </xsl:when>
        <xsl:otherwise>
            <xsl:text>NA</xsl:text>
        </xsl:otherwise>
    </xsl:choose>
    <xsl:text>|</xsl:text>
</xsl:template>

</xsl:stylesheet>

This is the first bit of XSL I have tried to write, so please excuse me if I am asking about something obvious or if I have used the wrong terminology...

Rog


---------------------------------------------------------
Insert witty interesting comment here...

mhkay July 5th, 2007 04:30 AM

Try something like this:

<xsl:template match="FLD">
  <xsl:variable name="sorted-CMPs" as="element()*">
     <xsl:perform-sort select="CMP">
        <xsl:sort select="@num"/>
     </xsl:perform-sort>
  </xsl:variable>
  <xsl:value-of select="(@*, $sorted-CMPs/@num,
      'NA', 'NA', 'NA', 'NA', 'NA')[position()=1 to 8]"
    separator="|"/>
  <xsl:text>#xa;</xsl:text>
</xsl:template>

>if I have used the wrong terminology...

XSLT doesn't deal with tags. People often use "tag" to mean "element", which is incorrect. Most elements have two tags, a start tag and an end tag. Use "element" if you mean element node, "node" if you want to include other kinds of node as well.

Michael Kay
http://www.saxonica.com/
Author, XSLT Programmer's Reference and XPath 2.0 Programmer's Reference

tall rog July 7th, 2007 06:30 AM

Hi Michael ,

 thanks for the suggestion. I tried to post a response yesterday, but for some reason the server appeared to be down. I can see how it is working. Creating a variable of ordered CMP elements. Then creating a sequence of CMP@num attributes with 5 'NA' strings to the end of the sequence. Then showing the first 5 of the combined sequence. It all makes sense however the ordering does not seem to work.

  I created the following...

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

<xsl:template match="FLD1">

      <xsl:variable name="sorted-CMPs" as="element()*">
         <xsl:perform-sort select="CMP">
            <xsl:sort select="@num"/>
         </xsl:perform-sort>
      </xsl:variable>

      <xsl:value-of select="($sorted-CMPs/@num,
          'NA', 'NA', 'NA', 'NA', 'NA')[position()=1 to 5]"
        separator="|"/>
      <xsl:text>|</xsl:text>

</xsl:template>

</xsl:stylesheet>

And the result I get is..

    11|13|12|14|NA|

    21|23|22|NA|NA|

    02|01|NA|NA|NA|

So the CMP elements are not sorted and also two new lines seem to be thrown even though there is no:
    <xsl:text>#13;</xsl:text>

So I thought that possibly this was a problem with Saxon (V8.9j). So I installed Xalan (j_2_7_0) but that won't even accept the 'as' attribute in the <xsl:variable> element.

I will spend more time on this over the weekend, I clearly have a lot more reading to do...

Rog


---------------------------------------------------------
Insert witty interesting comment here...

mhkay July 7th, 2007 10:28 AM

My mistake. Path expressions always sort into document order so $sorted-CMPs/@num undoes all your attempts at sorting. Change it to:

      <xsl:variable name="sorted-CMPs" as="element()*">
         <xsl:perform-sort select="CMP/@num">
            <xsl:sort select="."/>
         </xsl:perform-sort>
      </xsl:variable>

      <xsl:value-of select="($sorted-CMPs,
          'NA', 'NA', 'NA', 'NA', 'NA')[position()=1 to 5]"
        separator="|"/>
      <xsl:text>|</xsl:text>

The unwanted newlines are probably copied from the source document. Try <xsl:strip-space elements="*"/>.

Michael Kay
http://www.saxonica.com/
Author, XSLT Programmer's Reference and XPath 2.0 Programmer's Reference

tall rog July 9th, 2007 05:50 PM

Mike,

  thanks a lot... I got it working by using...
      <xsl:variable name="sorted-CMPs" as="attribute()*">
  otherwise I got a type error..

  The fact that if you use a path then it always accesses the nodes in document order is rather nasty. Dare I say... not logical. If I create an variable with ordered elements then I expect them to be ordered no matter how I access the nodes.

So if I use the following test input XML...

<TEST>
    <FLD1 attr1='attr11' attr2='attr12' attr3='attr13'>
        <CMP num='11'>11</CMP>
        <CMP num='13'>13</CMP>
        <CMP num='12'>12</CMP>
        <CMP num='14'>14</CMP>
    </FLD1>

    <FLD1 attr1='attr24' attr2='attr25' attr3='attr26'>
        <CMP num='21'>21</CMP>
        <CMP num='23'>23</CMP>
        <CMP num='22'>22</CMP>
    </FLD1>

    <FLD1 attr1='attr04' attr2='attr05' attr3='attr06'>
        <OTH num='OTH'/>
        <CMP num='02'>02</CMP>
        <CMP num='01'>01</CMP>
    </FLD1>
</TEST>


THIS DOES NOT WORK..
Because I am selecting an an attribute of the elements contain in the variable using a path.
      <xsl:variable name="sorted-CMPs" as="element()*">
         <xsl:perform-sort select="CMP">
            <xsl:sort select="./@num"/>
         </xsl:perform-sort>
      </xsl:variable>

      <xsl:value-of select="($sorted-CMPs/@num,
          'NA', 'NA', 'NA', 'NA', 'NA')[position()=1 to 5]"
        separator="|"/>
      <xsl:text>|</xsl:text>


THIS DOES WORK (assuming I wanted to order the value of the CMP elements and not on the num attribute of the CMP element)
It works because I am not accessing the value of the elements contain in the variable using a path.
      <xsl:variable name="sorted-CMPs" as="element()*">
         <xsl:perform-sort select="CMP">
            <xsl:sort select="."/>
         </xsl:perform-sort>
      </xsl:variable>

      <xsl:value-of select="($sorted-CMPs,
          'NA', 'NA', 'NA', 'NA', 'NA')[position()=1 to 5]"
        separator="|"/>
      <xsl:text>|</xsl:text>


AND THIS WORKS AS WELL
Because in the same manner, I am selecting the content of the variable without using a path.
      <xsl:variable name="sorted-CMPs" as="attribute()*">
         <xsl:perform-sort select="CMP/@num">
            <xsl:sort select="."/>
         </xsl:perform-sort>
      </xsl:variable>

      <xsl:value-of select="($sorted-CMPs,
          'NA', 'NA', 'NA', 'NA', 'NA')[position()=1 to 5]"
        separator="|"/>
      <xsl:text>|</xsl:text>


  It just seem wrong. To the point that I would be reporting this as a bug in Saxon if I had come across this by myself. I am sure there is or at least was a good reason for it. But I sure as hell can not see what it is.

Roger




---------------------------------------------------------
Insert witty interesting comment here...

mhkay July 9th, 2007 05:59 PM

>I am sure there is or at least was a good reason for it.

Yes, there certainly was. The usual combination of factors - a mix of backwards compatibility with XPath 1.0 and the fact that there are many use cases where document order is clearly the right thing to do.

Personally, I pushed hard for an operator other than "/" that did a simple mapping of sequences without reordering, and I didn't get it because people pointed out that the "for" expression and "/" between them could do everything that was needed. You just have to learn how to use them.

Michael Kay
http://www.saxonica.com/
Author, XSLT Programmer's Reference and XPath 2.0 Programmer's Reference


All times are GMT -4. The time now is 03:06 PM.

Powered by vBulletin®
Copyright ©2000 - 2020, Jelsoft Enterprises Ltd.
© 2013 John Wiley & Sons, Inc.