XSLT: remove everything except the first occurrence of a given node

I have XML something like this:

<MyXml>  
  <RandomNode1>  
    <TheNode>  
      <a/>  
      <b/>  
      <c/>  
    </TheNode>  
  </RandomeNode1>  
  <RandomNode2>  
  </RandomNode2>  
  <RandomNode3>  
    <RandomNode4>  
      <TheNode>  
        <a/>  
        <b/>  
        <c/>  
      </TheNode>    
    </RandomNode4>  
  </RandomNode3>  
</MyXml>

      

Where it <TheNode>

appears throughout XML, but not at the same level, often deep within other nodes. What I need to do is exclude all occurrences <TheNode>

EXCEPT the first one. The rest are redundant and take up space. What would be the XSL that could do this?

I have something like this:

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

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

  <xsl:template match="//TheNode[position()!=1]">
  </xsl:template>

</xsl:stylesheet>

      

But this is not true. Any suggestions?

+2


source to share


3 answers


<?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output method="xml" indent="yes"/>
  <xsl:template match="node() | @*">
    <xsl:copy>
      <xsl:apply-templates select="node() | @*"/>
    </xsl:copy>
  </xsl:template>
  <xsl:template match="TheNode[preceding::TheNode]"/>
</xsl:stylesheet>

      



+4


source


//TheNode[position()!=1]

doesn't work because here position()

refers to the parent context of each <TheNode>

. It will select all <TheNode>

that are not the first within their respective parent.

But you were on the right track. What did you mean:

(// TheNode) [position ()! = 1]

Note the parentheses - they cause the predicate to be applied to the entire selected node-set, rather than to each node individually.



Unfortunately, while this is a valid XPath expression, it is not valid as a match pattern. The match pattern must be meaningful (applicable) for an individual node, it cannot be a selected expression.

So, @Alohci's solution,

// TheNode [preceding :: TheNode]

is the correct way to express what you want.

+2


source


Another approach to template would be:

<xsl:template match="TheNode[generate-id()
                             != generate-id(/descendant::TheNode[1)]"/>

      

Note . It is more likely that an absolute expression gets an optimized representation relative to a relative expression likepreceding::TheNode

0


source







All Articles