XSLT sort based on attributes

Discussion in 'XML' started by Darren Davison, Jan 17, 2005.

  1. given the following DOM snippet;

    <root>
    <sub1 foo="4">testing</sub1>
    <sub1 foo="0">hello</sub1>
    <sub1 foo="0">world</sub1>
    <sub1>hello again</sub1>
    </root>

    I need to transform with XSL to something like;

    start foo = 0
    hello
    world
    end foo = 0

    start foo = 4
    testing
    end foo = 4

    hello again


    The (hopefully clear) constraints are that the attribute foo is
    optional and that it may be any whole number if present, though may or
    may not be sequential.

    Sorting the nodeset based on the value of foo I can manage (!), but I
    can't figure out how to output the boundary markers shown. Can anyone
    help??

    Many TIA!
     
    Darren Davison, Jan 17, 2005
    #1
    1. Advertising

  2. Darren Davison

    Ed Beroset Guest

    Darren Davison wrote:
    > given the following DOM snippet;
    >
    > <root>
    > <sub1 foo="4">testing</sub1>
    > <sub1 foo="0">hello</sub1>
    > <sub1 foo="0">world</sub1>
    > <sub1>hello again</sub1>
    > </root>
    >
    > I need to transform with XSL to something like;
    >
    > start foo = 0
    > hello
    > world
    > end foo = 0
    >
    > start foo = 4
    > testing
    > end foo = 4
    >
    > hello again
    >
    >
    > The (hopefully clear) constraints are that the attribute foo is
    > optional and that it may be any whole number if present, though may or
    > may not be sequential.
    >
    > Sorting the nodeset based on the value of foo I can manage (!), but I
    > can't figure out how to output the boundary markers shown. Can anyone
    > help??


    I think so. Try this:

    <?xml version="1.0" encoding="iso-8859-1"?>
    <xsl:stylesheet version="1.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xsd="http://www.w3.org/2001/XMLSchema"
    >

    <xsl:eek:utput method="text" indent="yes"/>


    <xsl:template match="/">
    <xsl:apply-templates select="root/sub1[@foo]">
    <xsl:sort select="@foo" order="ascending"/>
    </xsl:apply-templates>
    <xsl:apply-templates select="root/sub1[not(@foo)]">
    <xsl:sort select="@foo" order="ascending"/>
    </xsl:apply-templates>
    </xsl:template>


    <xsl:template match="sub1">
    <xsl:if test="not(@foo=preceding::node()/@foo)">
    <xsl:text>


    </xsl:text>
    <xsl:if test="@foo">
    <xsl:text>start foo = </xsl:text>
    <xsl:value-of select="@foo"/>
    </xsl:if>
    </xsl:if>
    <xsl:text>
    </xsl:text>
    <xsl:value-of select="."/>
    <xsl:if test="not(@foo=following::node()/@foo)">
    <xsl:text>
    </xsl:text>
    <xsl:if test="@foo">
    <xsl:text>end foo = </xsl:text>
    <xsl:value-of select="@foo"/>
    </xsl:if>
    </xsl:if>
    </xsl:template>


    </xsl:stylesheet>

    When I run that on the data you've posted, I get this output:


    start foo = 0
    hello
    world
    end foo = 0

    start foo = 4
    testing
    end foo = 4


    hello again

    That's pretty close to what you wanted, if I understood you correctly.
    If not, you can probably see how to modify it. If you don't actually
    need the non-foo nodes at the end (and could accept them at beginning of
    the output) then you can just elimate the second apply-templates in the
    root template and change the select clause of the first one to be just
    root/sub1.

    Ed
     
    Ed Beroset, Jan 17, 2005
    #2
    1. Advertising

  3. This is a standard grouping guestion, see
    http://www.jenitennison.com/xslt/grouping

    something like

    <xsl:key name="x" match="sub1" use="foo"/>

    <xsl:template match="root">
    <xsl:for-each select="sub1[generate-id()=generate-id(key('x',@foo))]">
    <xsl:sort select="@foo" data-type="number"/>
    <xsl:if test="@foo">start foo = <xsl:value-of select="@foo"/></xsl:if>
    <xsl:for-each select="key('x',@foo)">
    <xsl:value-of select="."/>
    <xsl:text>
    </xsl:text>
    <xsl:if test="@foo">end foo = <xsl:value-of select="@foo"/></xsl:if>
    </xsl:for-each>
    </xsl:template>

    David
     
    David Carlisle, Jan 17, 2005
    #3
  4. David Carlisle wrote:

    > This is a standard grouping guestion, see
    > http://www.jenitennison.com/xslt/grouping


    looks like a pretty useful site - bookmarked!

    > something like
    >
    > <xsl:key name="x" match="sub1" use="foo"/>
    >
    > <xsl:template match="root">
    > <xsl:for-each select="sub1[generate-id()=generate-id(key('x',@foo))]">
    > <xsl:sort select="@foo" data-type="number"/>
    > <xsl:if test="@foo">start foo = <xsl:value-of select="@foo"/></xsl:if>
    > <xsl:for-each select="key('x',@foo)">
    > <xsl:value-of select="."/>
    > <xsl:text>
    </xsl:text>
    > <xsl:if test="@foo">end foo = <xsl:value-of select="@foo"/></xsl:if>
    > </xsl:for-each>
    > </xsl:template>


    Thanks very much for your help David

    :)


    --
    darren@ public key
    davisononline.org #DD356B0D
     
    Darren Davison, Jan 17, 2005
    #4
  5. Ed Beroset wrote:

    > I think so. Try this:


    - snip -

    > That's pretty close to what you wanted, if I understood you correctly.
    > If not, you can probably see how to modify it. If you don't actually
    > need the non-foo nodes at the end (and could accept them at beginning of
    > the output) then you can just elimate the second apply-templates in the
    > root template and change the select clause of the first one to be just
    > root/sub1.


    Many thanks for your help Ed.

    --
    darren@                public key
    davisononline.org      #DD356B0D
     
    Darren Davison, Jan 17, 2005
    #5
  6. Darren Davison schrieb:
    > David Carlisle wrote:
    >
    >
    >>This is a standard grouping guestion, see
    >>http://www.jenitennison.com/xslt/grouping

    > ...


    I was thinking about the Problem with the optional @foo which is not
    handled in Davids solution.

    I tried to solve this but ended up in a bit messy code :-(

    Are there more elegant solutions?

    Jo

    <?xml version="1.0" encoding="UTF-8"?>
    <xsl:stylesheet version="1.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:fo="http://www.w3.org/1999/XSL/Format">
    <xsl:key name="fooKey" match="sub1" use="@foo" />
    <xsl:eek:utput omit-xml-declaration="yes" method="text" />
    <xsl:preserve-space elements="/" />
    <xsl:template match="/">
    <xsl:apply-templates />
    </xsl:template>

    <xsl:template match="root">
    <xsl:for-each select="sub1[generate-id() =
    generate-id(key('fooKey',@foo)[1])] | sub1[not(@foo)]">
    <xsl:variable name="fooParm" select="@foo" />
    start foo=<xsl:value-of select="$fooParm" /><xsl:text>
    </xsl:text>
    <xsl:for-each select="../sub1[@foo = $fooParm or (not(@foo) and
    not($fooParm))]">
    <xsl:value-of select="." /><xsl:text>
    </xsl:text>
    </xsl:for-each>
    end foo=<xsl:value-of select="$fooParm" /><xsl:text>
    </xsl:text>
    </xsl:for-each>
    </xsl:template>

    <xsl:template match="sub1[@foo]">
    <xsl:value-of select="." />
    </xsl:template>
    </xsl:stylesheet>
     
    =?ISO-8859-1?Q?Joachim_Wei=DF?=, Jan 18, 2005
    #6
  7. I was thinking about the Problem with the optional @foo which is not
    handled in Davids solution.

    my code handles an omitted foo the same as foo="" Given that foo had
    numeric values I assumed that "" would not be a legal value and so
    no distinction between these cases was necessary.

    David
     
    David Carlisle, Jan 18, 2005
    #7
  8. I wrote

    > I was thinking about the Problem with the optional @foo which is not
    > handled in Davids solution.
    >
    > my code handles an omitted foo the same as foo="" Given that foo had
    > numeric values I assumed that "" would not be a legal value and so
    > no distinction between these cases was necessary.
    >


    although in fairness I should say there were some typos so actually my
    code wouldn't run at all, also the original poster wanted "" to sort
    high whereas by default it sorts low. The code below fixes the typos and
    negates the sort key so "" (NaN) sorts high.


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

    <xsl:key name="x" match="sub1" use="string(@foo)"/>

    <xsl:template match="root">
    <xsl:for-each select="sub1[generate-id()=generate-id(key('x',string(@foo)))]">
    <xsl:sort select="-@foo" data-type="number" order="descending"/>
    <xsl:if test="@foo">
    start foo = <xsl:value-of select="@foo"/></xsl:if>
    <xsl:for-each select="key('x',string(@foo))">
    <xsl:text>
    </xsl:text>
    <xsl:value-of select="."/>
    <xsl:text>
    </xsl:text>
    </xsl:for-each>
    <xsl:if test="@foo">
    end foo = <xsl:value-of select="@foo"/></xsl:if>
    </xsl:for-each>
    </xsl:template>

    </xsl:stylesheet>


    $ saxon8 sort.xml sort.xsl
    Warning: Running an XSLT 1.0 stylesheet with an XSLT 2.0 processor
    <?xml version="1.0" encoding="UTF-8"?>
    start foo = 0
    hello

    world

    end foo = 0
    start foo = 4
    testing

    end foo = 4
    hello again
     
    David Carlisle, Jan 18, 2005
    #8
  9. David Carlisle schrieb:
    > I wrote
    >
    >
    >> I was thinking about the Problem with the optional @foo which is not
    >> handled in Davids solution.
    >>
    >>my code handles an omitted foo the same as foo="" Given that foo had
    >>numeric values I assumed that "" would not be a legal value and so
    >>no distinction between these cases was necessary.

    > ...


    You 're right!

    I created a problem, where no problem exists.

    Jo
     
    =?ISO-8859-15?Q?Joachim_Wei=DF?=, Jan 18, 2005
    #9
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. vaibhav
    Replies:
    3
    Views:
    1,152
    vaibhav
    Aug 14, 2006
  2. Max
    Replies:
    1
    Views:
    484
    Joe Kesselman
    Sep 22, 2006
  3. P4trykx
    Replies:
    2
    Views:
    1,828
    bruce barker
    Jan 31, 2007
  4. Replies:
    7
    Views:
    745
    Stefan Arentz
    Sep 10, 2007
  5. Navin
    Replies:
    1
    Views:
    706
    Ken Schaefer
    Sep 9, 2003
Loading...

Share This Page