Wrox Programmer Forums

Need to download code?

View our list of code downloads.

Go Back   Wrox Programmer Forums > XML > XSLT
Password Reminder
Register
Register | FAQ | Members List | Calendar | Search | Today's Posts | Mark Forums Read
XSLT General questions and answers about XSLT. For issues strictly specific to the book XSLT 1.1 Programmers Reference, please post to that forum instead.
Welcome to the p2p.wrox.com Forums.

You are currently viewing the XSLT section of the Wrox Programmer to Programmer discussions. This is a community of tens of thousands of software programmers and website developers including Wrox book authors and readers. As a guest, you can read any forum posting. By joining today you can post your own programming questions, respond to other developersí questions, and eliminate the ads that are displayed to guests. Registration is fast, simple and absolutely free .
DRM-free e-books 300x50
Reply
 
Thread Tools Display Modes
  #1 (permalink)  
Old July 5th, 2007, 02:42 AM
Registered User
 
Join Date: Jul 2007
Location: Wroclaw, , Poland.
Posts: 3
Thanks: 0
Thanked 0 Times in 0 Posts
Send a message via MSN to tall rog
Default 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...
__________________
---------------------------------------------------------
Insert witty interesting comment here...
Reply With Quote
  #2 (permalink)  
Old July 5th, 2007, 04:30 AM
mhkay's Avatar
Wrox Author
Points: 17,570, Level: 57
Points: 17,570, Level: 57 Points: 17,570, Level: 57 Points: 17,570, Level: 57
Activity: 100%
Activity: 100% Activity: 100% Activity: 100%
 
Join Date: Apr 2004
Location: Reading, Berks, United Kingdom.
Posts: 4,800
Thanks: 0
Thanked 260 Times in 255 Posts
Default

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
Reply With Quote
  #3 (permalink)  
Old July 7th, 2007, 06:30 AM
Registered User
 
Join Date: Jul 2007
Location: Wroclaw, , Poland.
Posts: 3
Thanks: 0
Thanked 0 Times in 0 Posts
Send a message via MSN to tall rog
Default

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...
Reply With Quote
  #4 (permalink)  
Old July 7th, 2007, 10:28 AM
mhkay's Avatar
Wrox Author
Points: 17,570, Level: 57
Points: 17,570, Level: 57 Points: 17,570, Level: 57 Points: 17,570, Level: 57
Activity: 100%
Activity: 100% Activity: 100% Activity: 100%
 
Join Date: Apr 2004
Location: Reading, Berks, United Kingdom.
Posts: 4,800
Thanks: 0
Thanked 260 Times in 255 Posts
Default

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
Reply With Quote
  #5 (permalink)  
Old July 9th, 2007, 05:50 PM
Registered User
 
Join Date: Jul 2007
Location: Wroclaw, , Poland.
Posts: 3
Thanks: 0
Thanked 0 Times in 0 Posts
Send a message via MSN to tall rog
Default

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...
Reply With Quote
  #6 (permalink)  
Old July 9th, 2007, 05:59 PM
mhkay's Avatar
Wrox Author
Points: 17,570, Level: 57
Points: 17,570, Level: 57 Points: 17,570, Level: 57 Points: 17,570, Level: 57
Activity: 100%
Activity: 100% Activity: 100% Activity: 100%
 
Join Date: Apr 2004
Location: Reading, Berks, United Kingdom.
Posts: 4,800
Thanks: 0
Thanked 260 Times in 255 Posts
Default

>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
Reply With Quote
Reply


Thread Tools
Display Modes

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is Off
HTML code is Off
Trackbacks are Off
Pingbacks are On
Refbacks are Off

Similar Threads
Thread Thread Starter Forum Replies Last Post
How to get least value numbers in a sorted array? ashokparchuri Other Programming Languages 3 December 5th, 2006 08:25 AM
Urgent Issue to be sorted out mike_remember ASP.NET 1.0 and 1.1 Professional 5 November 1st, 2006 09:52 AM
sorted table crmpicco Javascript How-To 0 March 17th, 2005 09:29 AM
Dataset does not get sorted correctly Imar ADO.NET 2 December 29th, 2003 03:49 PM
Search in a sorted list yajleejnus Classic ASP Basics 0 June 11th, 2003 04:00 PM



All times are GMT -4. The time now is 11:59 PM.


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