Reputation: 13
this get ever more complicated :)
now i face another issue in last question we managed to take unique values from only one parent node
now with:
<?xml version="1.0" encoding="ISO-8859-1"?>
<roots>
<root>
<name>first</name>
<item>
<something>A</something>
<something>A</something>
</item>
<item>
<something>B</something>
<something>A</something>
</item>
<item>
<something>C</something>
<something>P</something>
</item>
<item>
<something>A</something>
<something>L</something>
</item>
<item>
<something>A</something>
<something>A</something>
</item>
<item>
<something>B</something>
<something>A</something>
</item>
<item>
<something>D</something>
<something>A</something>
</item>
</root>
<root>
<name>second</name>
<item>
<something>E</something>
<something>A</something>
</item>
<item>
<something>B</something>
<something>A</something>
</item>
<item>
<something>F</something>
<something>A</something>
</item>
<item>
<something>A</something>
<something>A</something>
</item>
<item>
<something>A</something>
<something>A</something>
</item>
<item>
<something>B</something>
<something>H</something>
</item>
<item>
<something>D</something>
<something>G</something>
</item>
</root>
</roots>
now i need to get the unique values depending only from one node before but just from the elements on the second position
<?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output indent="yes" method="text"/>
<xsl:key name="item-by-value" match="something"
use="concat(normalize-space(.), ' ', generate-id(./ancestor::root))"/>
<xsl:key name="rootkey" match="root" use="name"/>
<xsl:template match="/">
<xsl:for-each select="key('rootkey','first')">
<xsl:for-each select="item/something[1]">
<xsl:sort />
<xsl:if test="generate-id() = generate-id(key('item-by-value',
concat(normalize-space(.), ' ', generate-id(./ancestor::root))))">
<xsl:value-of select="."/>
</xsl:if>
</xsl:for-each>
<xsl:text>_________</xsl:text>
<xsl:for-each select="item/something[2]">
<xsl:sort />
<xsl:if test="generate-id() = generate-id(key('item-by-value',
concat(normalize-space(.), ' ', generate-id(./ancestor::root))))">
<xsl:value-of select="."/>
</xsl:if>
</xsl:for-each>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
with this XSL i get ABCD_________LP where the result i need is ABCD_________ALP
any ideas?
Upvotes: 1
Views: 924
Reputation: 52888
I know you're restricted to XSLT 1.0 and you're using Xalan, but I'm going to add this answer just in case it might help someone else doing a search in the future. (That can use XSLT 2.0.) Hope you don't mind.
Also, I'm using Saxon-HE 9.2.0.6 for the processor.
Here's the stylesheet:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
<xsl:output method="text"/>
<xsl:template match="/roots">
<xsl:for-each select="root">
<xsl:value-of select="distinct-values(item/something[1])"/>___<xsl:value-of select="distinct-values(item/something[2])"/>
<xsl:text>
</xsl:text>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
Here's the output using your XML:
A B C D___A P L
E B F A D___A H G
If you wanted to strip the spaces from the output, you could put the output in a variable and then use replace
to strip the spaces. (Anyone know of a better way?)
Stylesheet:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
<xsl:output method="text"/>
<xsl:template match="/roots">
<xsl:variable name="output">
<xsl:for-each select="root">
<xsl:value-of select="distinct-values(item/something[1])"/>___<xsl:value-of select="distinct-values(item/something[2])"/>
<xsl:text>
</xsl:text>
</xsl:for-each>
</xsl:variable>
<xsl:value-of select="replace($output,' ','')"/>
</xsl:template>
</xsl:stylesheet>
Output:
ABCD___APL
EBFAD___AHG
Upvotes: 0
Reputation: 23578
Once again, the issue is that if you want to say "the first node with this content under this root appearing in this position in the item
node", then you have to add "position in the item
node" to the key. You can either do this by having two separate keys, as Dimitre's solution does, or change your key to:
use="concat(normalize-space(.), ' ',
count(./preceding-sibling::something), ' ', generate-id(./ancestor::root))"/>
And then make your two test expressions look like:
<xsl:if test="generate-id() = generate-id(key('item-by-value',
concat(normalize-space(.), ' 0 ', generate-id(./ancestor::root))))">
and:
<xsl:if test="generate-id() = generate-id(key('item-by-value',
concat(normalize-space(.), ' 1 ', generate-id(./ancestor::root))))">
Upvotes: 1
Reputation: 243599
Just a slight modification to my answer to your previous question and you've got it!
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text"/>
<xsl:key name="kSomethingByNameAndVal-1" match="something[1]"
use="concat(../../name, '+', .)"/>
<xsl:key name="kSomethingByNameAndVal-2" match="something[2]"
use="concat(../../name, '+', .)"/>
<xsl:template match="/">
<xsl:for-each select="*/*">
<xsl:for-each select=
"item/something[1]
[generate-id()
=
generate-id(key('kSomethingByNameAndVal-1',
concat(../../name, '+', .)
)
)
]
">
<xsl:value-of select="."/>
</xsl:for-each>
<xsl:text>
</xsl:text>
<xsl:for-each select=
"item/something[2]
[generate-id()
=
generate-id(key('kSomethingByNameAndVal-2',
concat(../../name, '+', .)
)
)
]
">
<xsl:value-of select="."/>
</xsl:for-each>
<xsl:text>
</xsl:text>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
When this transformation is applied on the provided XML document, The wanted, correct results are produced:
ABCD
APL
EBFAD
AHG
Upvotes: 1