Newbie XSL Question: Comparing to preceding-sibling within for-each

R

Red

Hi,

Pretty new to XML, but I have adopted some reports through my job that
I need to work with. I've got some training lined up next month, but
until then I have a few modifications to existing XSL files that I
need to make pretty sharpish.

We have certain reports that contain grouping and sums in the output.
The existing XSL just reads out all rows to html tables. I would like
to make these easier to read by eliminating all duplicates within a
row. At the moment, I dont have the access to change the XML output,
just the XSL. I hope the following example explains it well enough.
Any questions, let me know.

Thanks,

Red.



== XML ===========================================

<?xml version="1.0"?>
<rs:data>
<z:row BRANCH='Birmingham' ACCOUNT_NO='1001001' STATUS='Open'
BALANCE='5380.04'/>
<z:row BRANCH='Birmingham' ACCOUNT_NO='1001002' STATUS='Open'
BALANCE='1281.12'/>
<z:row BRANCH='London' ACCOUNT_NO='1001003' STATUS='Closed'
BALANCE='1015.32'/>
<z:row BRANCH='London' ACCOUNT_NO='1001004' STATUS='Open'
BALANCE='9866.53'/>
<z:row BRANCH='London' ACCOUNT_NO='1001005' STATUS='Open'
BALANCE='1659.55'/>
<z:row BRANCH='Glasgow' ACCOUNT_NO='1001006' STATUS='Open'
BALANCE='6944.21'/>
</rs:data>



== Current XSL ====================================

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/
Transform">
<xsl:template match="/">
<table>
<xsl:for-each select="//z:row">
<tr>
<td><xsl:value-of select="@BRANCH"/></td>
<td><xsl:value-of select="@ACCOUNT_NO"/></td>
<td><xsl:value-of select="@STATUS"/></td>
<td><xsl:value-of select="@BALANCE"/></td>
<tr>
</xsl:for-each>
</table>
</xsl:template>
</xsl:stylesheet>
<!--end-->



== Current Output =================================

Birmingham 1001001 Open 5380.04
Birmingham 1001002 Open 1281.12
London 1001003 Closed 1015.32
London 1001004 Open 9866.53
London 1001005 Open 1659.55
Glasgow 1001006 Open 6944.21


== Required output ================================

Birmingham 1001001 Open 5380.04
1001002 Open 1281.12
London 1001003 Closed 1015.32
1001004 Open 9866.53
1001005 Open 1659.55
Glasgow 1001006 Open 6944.21
 
J

Joe Kesselman

You might want to start by looking at the examples on Dave Pawson's XSLT
FAQ page
http://www.dpawson.co.uk/xsl/sect2/sect21.html

I haven't checked, but I'd be willing to bet that something in the
sorting or grouping category illustrates exactly this test.

(Though you've almost answered your own question: you want to use a
conditional based on the immediately preceeding sibling.)

General reminder: Any time you're thinking about using for-each, you
should seriously consider using apply-templates and a separate template
instead. That's usually a better solution.
 
P

Pavel Lepin

Red said:
We have certain reports that contain grouping and sums in
the output. The existing XSL just reads out all rows to
html tables. I would like to make these easier to read by
eliminating all duplicates within a row. At the moment, I
dont have the access to change the XML output, just the
XSL.

<?xml version="1.0"?>
<rs:data>

Namespace declarations seem to be missing.
Birmingham 1001001 Open 5380.04
1001002 Open 1281.12
London 1001003 Closed 1015.32
1001004 Open 9866.53
1001005 Open 1659.55
Glasgow 1001006 Open 6944.21

Generalising a bit, it's a trivial grouping problem. The
following is a working example of how it's done:

<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:rs="http://example.org/rs"
xmlns:z="http://example.org/z">
<xsl:eek:utput method="html"/>
<xsl:key name="branch" match="z:row" use="@BRANCH"/>
<xsl:key name="branches" match="z:row"
use="count(.|key('branch',@BRANCH)[1])=1"/>
<xsl:template match="rs:data">
<data>
<xsl:apply-templates
select="key('branches',true())" mode="branch"/>
</data>
</xsl:template>
<xsl:template match="z:row" mode="branch">
<xsl:apply-templates select="key('branch',@BRANCH)"/>
</xsl:template>
<xsl:template
match="z:row[count(.|key('branch',@BRANCH)[1])=1]">
<row>
<cell><xsl:apply-templates select="@BRANCH"/></cell>
<xsl:call-template name="acct-stat-bal"/>
</row>
</xsl:template>
<xsl:template match="z:row">
<row>
<cell></cell>
<xsl:call-template name="acct-stat-bal"/>
</row>
</xsl:template>
<xsl:template name="acct-stat-bal">
<cell>
<xsl:apply-templates select="@ACCOUNT_NO"/>
</cell>
<cell>
<xsl:apply-templates select="@STATUS"/>
</cell>
<cell>
<xsl:apply-templates select="@BALANCE"/>
</cell>
</xsl:template>
</xsl:stylesheet>

It gets a bit hairier if you need to combine grouping and
sorting. On the other hand, if you can use an
XSLT2-compliant processor, everything suddenly becomes very
easy as long as overusing sequences does not lead to
performance issues.

<irony intensity="0.75">

Note that for-each is evil and employing it where unneeded
may darn you to heck. If that training next month doesn't
mention it, I advise accusing your mentors of being
blasphemous for-each-worshippers, then preaching the
virtues of template-based approach. Who knows, they might
not be beyond redemption yet.

</irony>
 

Ask a Question

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

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,995
Messages
2,570,233
Members
46,820
Latest member
GilbertoA5

Latest Threads

Top