Reputation: 21
I try to transform in XSLT 2.0
<data>
<attributes>
<attribute code="Name" datatyp="String" class="A"></attribute>
<attribute code="Age" datatyp="Integer" class="A"></attribute>
<attribute code="sex" datatyp="String" class="A"></attribute>
<attribute code="height" datatyp="Integer" class="B"></attribute>
</attributes>
<personal>
<name>Klaus</name>
<Age>16</Age>
<sex>male</sex>
<height>180</height>
</personal>
<personal>
<height>165</height>
</personal>
</data>
into something like that for Person 1
<personal>
<class>
<classtype>A</classtype>
<name>Klaus</name>
<Age>16</Age>
<sex>male</sex>
</class>
<class>
<classtype>B</classtype>
<height>180</height>
</class>
</personal>
<personal>
<class>
<classtype>B</classtype>
<height>165</height>
</class>
</personal>
Where the dependencies which attribute appears in which class are defined in the beginning of the Input XML. I need to call the templates for class A and B only once per Person, that's why my attempts like the following are not working.
<xsl:for-each select="...personal/*">
<xsl:variable name="code"><xsl:value-of select="name()"></xsl:value-of> </xsl:variable>
<xsl:if test="//attribute[@code=$code]/@class='A'">
<!-- call template A -->
I need some help to solve the problem of calling the right templates according to the given parameters.
Upvotes: 2
Views: 103
Reputation: 243529
Simpler, more than twice shorter, preserving order and really correct:
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:key name="kAttrByCode" match="attribute" use="@code"/>
<xsl:template match="personal">
<xsl:copy>
<xsl:for-each-group select="*[key('kAttrByCode', name())]"
group-adjacent="key('kAttrByCode', name())/@class">
<class>
<classtype><xsl:sequence select="current-grouping-key()"/></classtype>
<xsl:sequence select="current-group()"/>
</class>
</xsl:for-each-group>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
When this transformation is applied on the provided XML document (note that code="Name"
is corrected to code="name"
-- otherwise a case conversion function must be used to achieve case insensibility):
<data>
<attributes>
<attribute code="name" datatyp="String" class="A"></attribute>
<attribute code="Age" datatyp="Integer" class="A"></attribute>
<attribute code="sex" datatyp="String" class="A"></attribute>
<attribute code="height" datatyp="Integer" class="B"></attribute>
</attributes>
<personal>
<name>Klaus</name>
<Age>16</Age>
<sex>male</sex>
<height>180</height>
</personal>
<personal>
<height>165</height>
</personal>
</data>
the wanted, correct result is produced:
<personal>
<class>
<classtype>A</classtype>
<name>Klaus</name>
<Age>16</Age>
<sex>male</sex>
</class>
<class>
<classtype>B</classtype>
<height>180</height>
</class>
</personal>
<personal>
<class>
<classtype>B</classtype>
<height>165</height>
</class>
</personal>
Explanation:
Using the <xsl:for-each-group>
instruction with the attribute group-adjacent
Using the standard XSLT functions current-group()
and current-grouping-key()
Upvotes: 0
Reputation: 117073
Would this work for you:
XSLT 2.0
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:key name="attr-by-code" match="attribute" use="@code" />
<!-- identity transform -->
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="personal">
<xsl:copy>
<xsl:for-each-group select="*" group-by="key('attr-by-code', name())/@class">
<class>
<classtype>
<xsl:value-of select="current-grouping-key()" />
</classtype>
<xsl:apply-templates select="current-group()" />
</class>
</xsl:for-each-group>
</xsl:copy>
</xsl:template>
<xsl:template match="attributes"/>
</xsl:stylesheet>
Upvotes: 1