469,364 Members | 2,346 Online
Bytes | Developer Community
New Post

Home Posts Topics Members FAQ

Post your question to a community of 469,364 developers. It's quick & easy.

XSLT sort based on attributes

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!
Jul 20 '05 #1
8 3239
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:output 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

Jul 20 '05 #2

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
Jul 20 '05 #3
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

Jul 20 '05 #4
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
Jul 20 '05 #5
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:output 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>

Jul 20 '05 #6

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
Jul 20 '05 #7

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
Jul 20 '05 #8
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

Jul 20 '05 #9

This discussion thread is closed

Replies have been disabled for this discussion.

Similar topics

2 posts views Thread by Jon Martin Solaas | last post: by
3 posts views Thread by Teksure | last post: by
7 posts views Thread by Harolds | last post: by
4 posts views Thread by David S. Alexander | last post: by
3 posts views Thread by thomas.porschberg | last post: by
reply views Thread by zhoujie | last post: by
reply views Thread by suresh191 | last post: by
1 post views Thread by Marylou17 | last post: by
By using this site, you agree to our Privacy Policy and Terms of Use.