tags:

views:

73

answers:

4

This is my input xml:

<root>
   <node1/>
   <node2/>
   <node3/>
   <node4/>
   <othertags/>
</root>

The output must be:

<root>
   <othertags/>
</root>

if any of the 4 nodes isn't null then none of the tags must be dropped.
example:

<root>
   <node1/>
   <node2/>
   <node3/>
   <node4>sample_text</node4>
   <othertags/>
</root>

Then the output must be same as input xml.

<root>
   <node1/>
   <node2/>
   <node3/>
   <node4>sample_text</node4>
   <othertags/>
</root>

This is the XSL code I have designed ::

  <xsl:template match="@*|node()">
    <xsl:copy>
      <xsl:apply-templates select="@*|node()"/>
    </xsl:copy>
  </xsl:template>
<xsl:template match="/root/node1[.='' and ../node2/.='' and ../node3/.='' and ../node4/.='']
             |/root/node2[.='' and ../node1/.='' and ../node3/.='' and ../node4/.='']
             |/root/node3[.='' and ../node1/.='' and ../node2/.='' and ../node4/.='']
             |/root/node4[.='' and ../node1/.='' and ../node2/.='' and ../node3/.='']"/>

As you can see the code requires more effort and becomes more bulky as the number of nodes increase. Is there any alternative way to overcome this bottleneck?

A: 

Mode "filter-those-nodes" checks the condition, and "not-those-nodes" removes the unwanted nodes. Unfiltered nodes will match one of the modeless apply-templates as if we just said '` inside the 'root' template.

<xsl:template match="root">
    <xsl:copy>
        <xsl:apply-templates mode="filter-those-nodes"/>
    </xsl:copy>
</xsl:template>

<xsl:template match="*" mode="filter-those-nodes">
    <xsl:choose>
        <xsl:when test="'' = concat(node1, node2, node3, node4)">
            <xsl:apply-templates mode="filter-those-nodes"/>
        </xsl:when>
        <xsl:otherwise>
            <xsl:apply-templates/>
        </xsl:otherwise>
    </xsl:choose>
</xsl:template>

<xsl:template match="node1 | node2 | node3 | node4" mode="not-those-nodes">
    <!-- filtered out -->
</xsl:template>

<xsl:template match="*" mode="not-those-nodes">
    <xsl:apply-templates select="." />
</xsl:template>
Lachlan Roche
@Lachlan-Roche, hi, sorry but in this case, its even more bottleneck kind of thing to follow hierarchical approach. because, these set of nodes appear in many different places. And also usage of <template match="*"> doesn't allow usage of anyother templates .. I have more than 15 templates meant for different jobs.
infant programmer
+2  A: 

Have you tried (untested)

<xsl:template match="node1|node2|node3|node4">
  <xsl:if test="
    (preceding-sibling::*|.|following-sibling::*)[
      self::node1 or self::node2 or self::node3 or self::node4
    ][.!='']
  ">
    <xsl:copy-of select="." />
  </if>
</xsl:template>
Tomalak
ya.. there is one flaw, I have fixed it .. As I don't have 2000 points I can't edit the post, pls do it by urself, => Replace if statement by, <xsl:if test=".!='' or ( (preceding-sibling::*|following-sibling::*)[ self::node1 or self::node2 or self::node3 or self::node4 ][.!='']) ">
infant programmer
reason :: your code tests only the sibling-nodes values of a particular node whether they are null, so had to add `.='' or` to test the value of self .. thanks for the helpful answer :-)
infant programmer
@infant programmer Note the . at the start of Tomalak's expression, this tests self as well as the siblings. I get the same result from both versions of the test.
Lachlan Roche
@lachlan-roche, he just now has edited it :-)
infant programmer
@infant: Yes I have edited it. My hope was you would not yet have tried it out. ;-) I've made it a little more obvious (and logical) now by placing the dot in the middle.
Tomalak
@Tomolak, yup thanx :-)
infant programmer
A: 
Dimitre Novatchev
A: 
<xsl:template match="/root/node()[name()='node1' or name()='node2' or name()='node3' or name()='node4']
[../node1/.='' and ../node2/.='' and ../node3/.='' and ../node4/.='']"/>
infant programmer
Good, but what if you have many hundreds of nodes and the node-set is constructed dynamically? The FXSL solution works OK in this case, too.
Dimitre Novatchev