Use XSLT to transform XML with conditional expressions on node count

I am trying to remove nodes from an XML file. Using only one XSLT for each of my XML, I need to make decisions in XSLT based on the number of children of the document.

<root>
  <branch>
    <foo>bar</foo>
  </branch>
<root>

      

should transform into

  <branch>
  </branch>

      

but

<root>
  <branch>
    <foo>bar</foo>
  </branch>
  <branch>
    <foo>baz</foo>
<root>

      

in

<root>
  <branch>
  </branch>
  <branch>
  </branch>
<root>

      

That is, the root element must be removed if its (only) child can act as a new document root from the XML result of the result after XSLT is applied. Nodes <foo>

must be removed in all cases.

Is there a way to accomplish this operation using XSL alone?

+3


source to share


2 answers


Try



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

<xsl:template match="root[*[2]]">
  <xsl:copy>
    <xsl:apply-templates/>
  </xsl:copy>
</xsl:template>

<xsl:template match="root[* and not(*[2])]">
  <xsl:apply-templates/>
</xsl:template>

<xsl:template match="branch/foo"/>

      

+2


source


Simpler, shorter and more general (no hardcoding of element names) :

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

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

 <xsl:template match="/*[not(*[2])]">
  <xsl:apply-templates/>
 </xsl:template>

 <xsl:template match="/*/*/node()"/>
</xsl:stylesheet>

      

when this transformation is applied to the first provided XML document (fixed to be done correctly):

<root>
    <branch>
        <foo>bar</foo>
    </branch>
</root>

      

required, the correct result is obtained :

<branch></branch>

      

When the same transformation is applied to the second provided XML document (again needed to be adjusted for correctness):

<root>
    <branch>
        <foo>bar</foo>
    </branch>
    <branch>
        <foo>baz</foo>
    </branch>
</root>

      

again required, the correct output is obtained :

<root>
    <branch></branch>
    <branch></branch>
</root>

      



Explanation

  • the identity rule copies each node "as is".

  • There are two templates that override the identity template for specific nodes and treat these nodes differently.

  • The first overriding pattern matches the top element that does not have a second child. It does not copy the element itself, but processes its children.

  • The second override pattern matches any element that is the grand child of the top element. This template has no body, which means that all such matched elements are ignored and not included in the output (in other words, "removed")

Please note :

This transformation can be applied to any XML document, regardless of the element names in it, and still produces the desired, correct result.

For example, when applied in this XML document:

<t>
    <b>
        <f>brrr</f>
    </b>
    <b>
        <f>bzzz</f>
    </b>
</t>

      

required, the correct result is obtained :

<t>
    <b></b>
    <b></b>
</t>

      

Compare this to the result obtained in the currently accepted answer :

<t>
    <b>
        <f>brrr</f>
    </b>
    <b>
        <f>bzzz</f>
    </b>
</t>

      

+2


source







All Articles