Reputation: 33
I want to do a transformation from XML to text combining some elements, but avoiding duplicates in the output. The XML would be something like that:
<A>
<B>
<param1>value0</param1>
<param2>value1</param2>
</B>
<B>
<param1>value2</param1>
<param2>value3</param2>
</B>
<C>
<param3>valueC1</param3>
<D>
<param4>value0</param4>
<param5>value4</param5>
</D>
<D>
<param4>value0</param4>
<param5>value5</param5>
</D>
<D>
<param4>value2</param4>
<param5>value6</param5>
</D>
</C>
<C>
<param3>valueC2</param3>
<D>
<param4>value0</param4>
<param5>value5</param5>
</D>
</C>
</A>
And the output:
OBJECT: param1=value0, param2=value1, param3=valueC1, param4=value0;
OBJECT: param1=value2, param2=value3, param3=valueC1, param4=value2;
OBJECT: param1=value0, param2=value1, param3=valueC2, param4=value0;
Notes:
I looked for some similar question, but I couldn't find any in the same case.
I guess that it could be done by using keys, but it's too complex.
Thanks!
Regards, Ale.
PS: Sorry for my English.
Upvotes: 3
Views: 200
Reputation: 122414
Given that you're not making any use of param5
in your output it would appear to be possible to simplify the problem to
param1
matches the param4
of any of the contained DsThis is one way to achieve that using templates.
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="text" />
<xsl:key name="BbyParam1" match="B" use="param1" />
<xsl:template match="/">
<xsl:apply-templates select="A/C" />
</xsl:template>
<xsl:template match="C">
<xsl:apply-templates select="key('BbyParam1', D/param4)">
<xsl:with-param name="currentC" select="." />
</xsl:apply-templates>
</xsl:template>
<xsl:template match="B">
<xsl:param name="currentC" />
<xsl:text>OBJECT: param1=</xsl:text>
<xsl:value-of select="param1" />
<xsl:text>, param2=</xsl:text>
<xsl:value-of select="param2" />
<xsl:text>, param3=</xsl:text>
<xsl:value-of select="$currentC/param3" />
<xsl:text>, param4=</xsl:text>
<xsl:value-of select="param1" />
<xsl:text> </xsl:text>
</xsl:template>
</xsl:stylesheet>
"find all distinct B elements whose param1
matches the param4
of any of the contained Ds" is actually very straightforward due to the fact that when you pass a node set as the second argument to the key
function it does precisely this - it returns the set of all nodes whose key value is the string value of any of the nodes in the argument node set, and the returned node set (being a set) is guaranteed to contain no duplicates.
Upvotes: 3
Reputation: 101748
This should do the trick:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text" indent="yes"/>
<xsl:key name="kB" match="B" use="param1" />
<xsl:key name="kD" match="D"
use="concat(param4, '+', generate-id(..))"/>
<xsl:template match="/">
<xsl:apply-templates select="A/C" />
</xsl:template>
<xsl:template match="C">
<xsl:apply-templates select="D[key('kB', param4) and
generate-id() =
generate-id(key('kD',
concat(
param4,
'+',
generate-id(..)
)
)[1])]" />
</xsl:template>
<xsl:template match="D">
<xsl:value-of
select="concat('OBJECT: param1=',
key('kB', param4)/param1,
', param2=',
key('kB', param4)/param2,
', param3=',
../param3,
', param4=',
param4,
'
')"/>
</xsl:template>
</xsl:stylesheet>
Output when run on your sample:
OBJECT: param1=value0, param2=value1, param3=valueC1, param4=value0
OBJECT: param1=value2, param2=value3, param3=valueC1, param4=value2
OBJECT: param1=value0, param2=value1, param3=valueC2, param4=value0
Upvotes: 0