XPath 1.0 select individual siblings attribute

I hunted around, but could not get any of the ideas that I found work.

These are the few nodes that I have in the XML file (which is generated from 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 a separate ATTR3 from each of them.

This currently works for the first //PANELS/PANEL[not(@ATTR3 = (preceding::*/@ATTR3))] and returns the expected result for '31'

But when I try to do the same for the second, it returns nothing (I want it to return "31" again) //ZONES/ZONE[not(@ATTR3 = (preceding::*/@ATTR3))]

I understand that the second one does not work, because the ATTR3 value for all of them is the same, but how to get a separate attribute value for node?

(This is used as a predicate for each of them, which I use to display each individual value)

It is used as 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 wish he was back

 PANELS: 31 ZONES: 31 

I tried using preceding-sibling instead of preceding , but then I get

 PANELS: 31, 31 ZONES: 31 

Each of them is in the template as follows:

  <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> 
+2
source share
2 answers

The following XSLT uses preceding-siblings instead of preceding and therefore produces the correct number of repetitions 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> 

Result 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> 

as follows:

  ZONES: 31 PANELS: 31 
+3
source

This short (13 lines) and simple conversion is just one template, without hard-coded lines :

 <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:output method="text" /> <xsl:strip-space elements="*"/> <xsl:template match="t/*[*/@ATTR3]"> <xsl:value-of select="concat(name(), ':')"/> <xsl:for-each select="*/@ATTR3[not(. = ../preceding-sibling::*/@ATTR3)]"> <xsl:value-of select="concat(' ', .)"/> </xsl:for-each> <xsl:text> </xsl:text> </xsl:template> </xsl:stylesheet> 

when applied to the provided XML (wrapped in one top element to become well-formed) a document :

 <t> <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> </t> 

creates the desired, correct result:

 PANELS: 31 ZONES: 31 
+1
source

All Articles