pml
pml

Reputation: 77

Run two templates for same node (XSLT)

I have some xml files like the following. They contain all different tree structures and some elements do have attributes.

<root>
  <element n="A">
    <element n="B">
      <attribute a="1"/>
      <attribute a="2"/>
    </element>
    <element n="C">
      <element n="D">
        <attribute a="3"/>
      </element>
    </element>
  </element>
</root>

I want to transform these files using XSLT to get the following output. I have to keep the tree structure and also create a list of all elements with their attributes:

<root>
  <structure>
    <newElement n="A">
      <newElement n="B">
        <newAttribute a="1"/>
        <newAttribute a="2"/>
      </newElement>
      <newElement n="C">
        <newElement n="D">
          <newAttribute a="3"/>
        </newElement>
      </newElement>
    </newElement>
  </structure>
  <list>
    <listElement n="A"/>
    <listElement n="B">
      <listAttribute a="1"/>
      <listAttribute a="2"/>
    </listElement>
    <listElement n="C"/>
    <listElement n="D">
      <listAttribute a="3"/>
    </listElement>
  </list>
</root>

I try to run two different templates "e1" and "e2" for one node "element" but it doesn't work. It seems that the first template is ignored. So what do I have to change?

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

<xsl:template match="/">
  <root>
    <structure>
      <xsl:apply-templates name="e1"/>
    </structure>
    <list>
      <xsl:apply-templates name="e2"/>
    </list>
  </root>
</xsl:template>

<xsl:template match="element" name="e1">
  <newElement>
    <xsl:attribute name="n">
      <xsl:value-of select="@n"/>
    </xsl:attribute>
    <xsl:apply-templates name="a1"/>
    <xsl:apply-templates name="e1"/>
  </newElement>
</xsl:template>

<xsl:template match="attribute" name="a1">
  <newAttribute>
    <xsl:attribute name="a">
      <xsl:value-of select="@a"/>
    </xsl:attribute>
  </newAttribute>
</xsl:template>

<xsl:template match="element" name="e2">
  <listElement>
    <xsl:attribute name="n">
      <xsl:value-of select="@n"/>
    </xsl:attribute>
    <xsl:apply-templates name="a2"/>
  </listElement>
  <xsl:apply-templates select="element"/>
</xsl:template>

<xsl:template match="attribute" name="a2">
  <listAttribute>
    <xsl:attribute name="a">
      <xsl:value-of select="@a"/>
    </xsl:attribute>
  </listAttribute>
</xsl:template>

</xsl:stylesheet>

Upvotes: 1

Views: 2763

Answers (1)

Tim C
Tim C

Reputation: 70638

It is not valid to use the name attribute on xsl:apply-templates. I am guessing your XSLT processing is simply ignoring the name in this case and is just dong a simple <xsl:apply-templates />.

I think what you need to use here is the mode attribute. When you use the mode attribute, it will only use matching templates with the same mode, allowing you to have two templates that match the same element.

Try this XSLT

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

<xsl:template match="/">
  <root>
    <structure>
      <xsl:apply-templates mode="e1"/>
    </structure>
    <list>
      <xsl:apply-templates mode="e2"/>
    </list>
  </root>
</xsl:template>

<xsl:template match="element" mode="e1">
  <newElement n="{@n}">
    <xsl:apply-templates mode="e1"/>
  </newElement>
</xsl:template>

<xsl:template match="attribute" mode="e1">
  <newAttribute a="{@a}"/>
</xsl:template>

<xsl:template match="element" mode="e2">
  <listElement n="{@n}">
    <xsl:apply-templates select="attribute" mode="e2"/>
  </listElement>
  <xsl:apply-templates select="element" mode="e2"/>
</xsl:template>

<xsl:template match="attribute" mode="e2">
  <listAttribute a="{@a}"/>
</xsl:template>
</xsl:stylesheet>

Note the use of "Attribute Value Templates" here, to simplify the XSLT.

Upvotes: 7

Related Questions