user3036309
user3036309

Reputation: 13

xsl transform remove double tags

I have som XML I need to transform to remove double tags.

Originally I used:

<xsl:template match="*[name()=name(../..)]"></xsl:template>
<xsl:template match="*[name()=name(../../..)]"></xsl:template>
<xsl:template match="*[name()=name(..)]">
    <xsl:apply-templates select="@*|node()"/>
</xsl:template>

Which together removed the inner of two tags with the same name. But that only works well if there is only one inner tag.

I have:

<a>
 <a>
    <b>xxxx</b>
    <c>xxxx</c>
 </a>
 <a>
    <b>yyyy</b>
    <c>yyyy</c>
 </a>
</a>

I want to end up with:

 <a>
    <b>xxxx</b>
    <c>xxxx</c>
 </a>
 <a>
    <b>yyyy</b>
    <c>yyyy</c>
 </a>

Rather than:

 <a>
    <b>xxxx</b>
    <c>xxxx</c>
    <b>yyyy</b>
    <c>yyyy</c>
 </a>

I know very little about XSL transform and the search syntax, so I hope someone can help me.

Upvotes: 1

Views: 158

Answers (2)

Ian Roberts
Ian Roberts

Reputation: 122364

Rather than removing the child when its name matches the parent it sounds like you really want to remove the parent when its name is the same as all its children:

<xsl:template match="@*|node()">
  <xsl:copy>
    <xsl:apply-templates select="@*|node()" />
  </xsl:copy>
</xsl:template>

<xsl:template match="*[*][not(*[name() != name(..)])]">
  <xsl:apply-templates select="@*|node()" />
</xsl:template>

You can read the second match expression as "any element X that has at least one child element and also does not have any child element with a name that is different from X".

Upvotes: 1

michael.hor257k
michael.hor257k

Reputation: 116982

The following stylesheet will remove the outer (parent) elements whose children all have the same name:

XSLT 1.0

<xsl:stylesheet version="1.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="*"/>

<!-- identity transform -->
<xsl:template match="@*|node()">
    <xsl:copy>
        <xsl:apply-templates select="@*|node()"/>
    </xsl:copy>
</xsl:template>

<!-- exception for parent whose children have all the same name -->
<xsl:template match="*[*][not(*[name()!=name(..)])]">
    <xsl:apply-templates select="@*|node()"/>
</xsl:template>

</xsl:stylesheet>

When the above is applied to the following test input:

<root>
    <a>
        <a>
            <b>bbb</b>
            <c>ccc</c>
        </a>
        <a>
            <d>
                <d>ddd</d>      
            </d>
            <e>eee</e>
            <f>
                <f>fff</f>  
                <g>ggg</g>  
            </f>
        </a>
    </a>
</root>

the result will be:

<?xml version="1.0" encoding="UTF-8"?>
<root>
   <a>
      <b>bbb</b>
      <c>ccc</c>
   </a>
   <a>
      <d>ddd</d>
      <e>eee</e>
      <f>
         <f>fff</f>
         <g>ggg</g>
      </f>
   </a>
</root>

Upvotes: 2

Related Questions