Sivabalakrishnan
Sivabalakrishnan

Reputation: 515

XSLT - Remove nodes when current node is equal to previous node

I need to write XSLT for an xml which contains in below format.

<books>
<book>
 <a>name</a>
 <a>name</a>
 <b>name</b>
 <b>name</b>
</book>
</books>

I need to eliminate the duplicate child nodes in some conditions.

  1. Only if(current node == previous node) then it should be removed.

ie.. if previous node (element) is <a> and current node (element) is also <a>, Then one node should be removed.

output for the above be,

`<a>name</a>`

`<b>name</b>`

please help me to do this.

Upvotes: 0

Views: 457

Answers (2)

Valdi_Bo
Valdi_Bo

Reputation: 30991

As I understood, you want to omit a leaf element (without children elements) if it has a previous sibling, which:

  • is also a leaf element,
  • has the same name,
  • has the same text content.

So the most intuitive solution (I think) is to write an empty template, matching just these nodes:

<xsl:template match="*[not(*)][preceding-sibling::*[1][not(*)]
  [name() = current()/name()][text() = current()/text()]]"/>

A brief description of the match attribute:

  • *[not(*)] - Every element without any child element (leaf element).
  • [ - Start of the second predicate.
    • preceding-sibling::*[1] - Take the first preceding sibling.
    • [not(*)] - It must not have any child element.
    • [name() = current()/name()] - It must have the same name as the "starting" element.
    • [text() = current()/text()] - It must have the same text as the "starting" element.
  • ] - End of the second predicate.

Of course, the script must contain also an identity template.

For a working example, with a bit extended source, see http://xsltransform.net/jxN8Nqm

If requirement concerning the same text is not necessary, delete the respective predicate fragment.

Upvotes: 1

Martin Honnen
Martin Honnen

Reputation: 167706

In XSLT 2 or 3 you can easily group adjacent sibling elements by their node name with for-each-group select="*" group-adjacent="node-name()" and simply output the first item in each group (which is equal to the context item .):

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

  <xsl:mode on-no-match="shallow-copy"/>

  <xsl:output method="xml" indent="yes"/>
  <xsl:strip-space elements="*"/>

  <xsl:template match="book">
      <xsl:copy>
          <xsl:for-each-group select="*" group-adjacent="node-name()">
              <xsl:copy-of select="."/>
          </xsl:for-each-group>
      </xsl:copy>
  </xsl:template>

</xsl:stylesheet>

https://xsltfiddle.liberty-development.net/6qVRKw4/1

Upvotes: 2

Related Questions