I have hunted around, but haven't been able to get any of the ideas I've found to work.
These are a couple of nodes I have in an xml file (that is generated from a db)
<PANELS>
<PANEL ATTR1="7" ATTR2="37" ATTR3="31"/>
<PANEL ATTR1="8" ATTR2="37" ATTR3="31"/>
<PANEL ATTR1="8A" ATTR2="37" ATTR3="31"/>
</PANELS>
<ZONES>
<ZONE ATTR1="7" ATTR2="37" ATTR3="31" />
<ZONE ATTR1="8" ATTR2="37" ATTR3="31" />
<ZONE ATTR1="8A" ATTR2="37" ATTR3="31" />
</ZONES>
I want to be able to select the distinct ATTR3 from each of these.
Currently, this works for the first one
//PANELS/PANEL[not(@ATTR3 = (preceding::*/@ATTR3))]
and returns the expected result for '31'
But when I try to do the same for the second one, it returns nothing (I want it to return '31' again)
//ZONES/ZONE[not(@ATTR3 = (preceding::*/@ATTR3))]
I understand that the second one is not working because the value of ATTR3 is the same for all of them, but how do I get the distinct attribute value per node?
(This is being used as the predicate for a for-each that I am using to display each distinct value)
This is being used like this, one of these for-each
for ZONES and one for PANELS
<xsl:for-each select="//PANELS/PANEL[not(@ATTR3 = (preceding::*/@ATTR3))]">
<xsl:sort select="@ATTR3"/>
<xsl:value-of select="@ATTR3" />
<xsl:if test="position()!=last()">, </xsl:if>
</xsl:for-each>
I would like it to return
PANELS: 31
ZONES: 31
I have tried using preceding-sibling
instead of preceding
, but then I get
PANELS: 31, 31
ZONES: 31
Each one is in a template like this:
<xsl:template match="//HEADER/ZONES" >
<fo:block font-size="10pt">
<fo:table table-layout="fixed" >
<fo:table-column column-width="proportional-column-width(1)"/>
<fo:table-column column-width="proportional-column-width(7)"/>
<fo:table-body>
<fo:table-row>
<fo:table-cell border-bottom="none">
<fo:block font-weight="bold">
<xsl:text>Zones:</xsl:text>
</fo:block>
</fo:table-cell >
<fo:table-cell>
<fo:block>
<xsl:for-each select="//HEADER/ZONES/ZONE[not(@ATTR3 = (preceding-sibling::*/@ATTR3))]">
<xsl:sort select="@ATTR3"/>
<xsl:value-of select="@ATTR3" />
<xsl:if test="position()!=last()">, </xsl:if>
</xsl:for-each>
</fo:block>
</fo:table-cell>
</fo:table-row>
</fo:table-body>
</fo:table>
</fo:block>
</xsl:template>
The following XSLT uses preceding-siblings
instead of preceding
and thus produces the correct number of repetitions of the 31:
<?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="//HEADER/ZONES">
ZONES:
<xsl:for-each select="//ZONES/ZONE[not(@ATTR3 = (preceding-sibling::*/@ATTR3))]">
<xsl:sort select="@ATTR3"/>
<xsl:value-of select="@ATTR3" />
<xsl:if test="position()!=last()">, </xsl:if>
</xsl:for-each>
</xsl:template>
<xsl:template match="//HEADER/PANELS">
PANELS:
<xsl:for-each select="//PANELS/PANEL[not(@ATTR3 = (preceding-sibling::*/@ATTR3))]">
<xsl:sort select="@ATTR3"/>
<xsl:value-of select="@ATTR3" />
<xsl:if test="position()!=last()">, </xsl:if>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
The output for this document:
<HEADER>
<PANELS>
<PANEL ATTR1="7" ATTR2="37" ATTR3="31"/>
<PANEL ATTR1="8" ATTR2="37" ATTR3="31"/>
<PANEL ATTR1="8A" ATTR2="37" ATTR3="31"/>
</PANELS>
<ZONES>
<ZONE ATTR1="7" ATTR2="37" ATTR3="31" />
<ZONE ATTR1="8" ATTR2="37" ATTR3="31" />
<ZONE ATTR1="8A" ATTR2="37" ATTR3="31" />
</ZONES>
</HEADER>
is as follows:
ZONES:
31
PANELS:
31