user1759737
user1759737

Reputation: 33

XSLT - How to delete tags and keep content?

I've got an XML file which is something like :

<doc>
  <element>
    <word>
      <text>one word</text>
    </word>
    <word>
      <text>two words</text>
    </word>
  </element>
  <element>
    <other>
      <text>the sky</text>
    </other>
    <word>
      <text>NN NN</text>
    </word>
  </element>
</doc>

And I'd like to have just one tag and the content of the lines when there are two on after the other, like that :

<element>
    <word>
      <text>one word</text>
       <text>two words</text>
    </word>
</element>

The problem is that I'm generating an <xsl:element> in order to get the <word> tags. And this <xsl:element> is already in a <xsl:for-each> loop that puts the <word> tags in the right order (filtering on an attribute).

My previous XML doc is like that :

<doc>
  <element class="word">
    <text>one word</text>
  </element>
  <element class="word">
    <text>NN NN</text>
  </element>
  <element class="word">
    <text>two words</text>
  </element>
  <element class="other">
    <text>the sky</text>
  </element>
</doc>

Any help would be great, thank you :)

-- More details --

This is the result I want to have :

<doc>
   <element>
      <word>
         <text>one word</text>
         <text>two words</text>
      </word>
   </element>
   <element>
      <other>
         <text>the sky</text>
      </other>
      <word>
         <text>NN NN</text>
      </word>
  </element>
</doc>

And I can't specify the tags word or other because I don't have a closed list of tags. So I'm using xsl:variable :

<xsl:for-each select="element">
    <xsl:variable name="name">
      <xsl:value-of select="@class"/>
    </xsl:variable>
    <xsl:element name="{$name}">
        <text>
          <xsl:value-of select="."/>
        </text>
    </xsl:element>
</xsl:for-each>

Upvotes: 0

Views: 1158

Answers (3)

Flynn1179
Flynn1179

Reputation: 12075

Try this:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:template match="element/*">
    <xsl:if test="not(preceding-sibling::*[name() = name(current())])">
      <xsl:copy>
        <xsl:apply-templates select="* | following-sibling::*[name()=name(current())]/*" />
      </xsl:copy>
    </xsl:if>
  </xsl:template>

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

It simply looks for any child nodes of element that do not have a previous sibling with the same name (i.e., the first of each one), and then applies templates to it's own child nodes and the child nodes of any following siblings with the same name.

Upvotes: 1

Dimitre Novatchev
Dimitre Novatchev

Reputation: 243459

This transformation:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>
 <xsl:strip-space elements="*"/>

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

 <xsl:template match="element/*"/>
 <xsl:template match="element/*[1]">
     <word>
       <xsl:apply-templates select="../*/*"/>
     </word>
 </xsl:template>
</xsl:stylesheet>

when applied on the provided XML document:

<doc>
    <element>
        <word>
            <text>one word</text>
        </word>
        <word>
            <text>two words</text>
        </word>
    </element>
    <element>
        <other>
            <text>the sky</text>
        </other>
        <word>
            <text>NN NN</text>
        </word>
    </element>
</doc>

produces (what I guess is) the wanted, correct result:

<doc>
   <element>
      <word>
         <text>one word</text>
         <text>two words</text>
      </word>
   </element>
   <element>
      <word>
         <text>the sky</text>
         <text>NN NN</text>
      </word>
   </element>
</doc>

Upvotes: 0

Daniel Haley
Daniel Haley

Reputation: 52848

You can "unwrap" an element by just using <xsl:apply-templates/>...

XML Input

<doc>
    <element>
        <word>
            <text>one word</text>
        </word>
        <word>
            <text>two words</text>
        </word>
    </element>
    <element>
        <other>
            <text>the sky</text>
        </other>
        <word>
            <text>NN NN</text>
        </word>
    </element>
</doc>

XSLT 1.0

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output indent="yes"/>
    <xsl:strip-space elements="*"/>

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

    <xsl:template match="element">
        <xsl:copy>
            <word>
                <xsl:apply-templates/>
            </word>
        </xsl:copy>
    </xsl:template>

    <xsl:template match="word|other">
        <xsl:apply-templates/>
    </xsl:template>

</xsl:stylesheet>

Output

<doc>
   <element>
      <word>
         <text>one word</text>
         <text>two words</text>
      </word>
   </element>
   <element>
      <word>
         <text>the sky</text>
         <text>NN NN</text>
      </word>
   </element>
</doc>

Upvotes: 0

Related Questions