Lacey
Lacey

Reputation: 21

XSL 2.0: Calling templates according to predefined attributes

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

Answers (2)

Dimitre Novatchev
Dimitre Novatchev

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:

  1. Using the <xsl:for-each-group> instruction with the attribute group-adjacent

  2. Using the standard XSLT functions current-group() and current-grouping-key()

Upvotes: 0

michael.hor257k
michael.hor257k

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

Related Questions