Question

I've got some XML, for example purposes it looks like this:

<root>
    <field1>test</field1>
    <f2>t2</f2>
    <f2>t3</f2>
</root>

I want to transform it with XSLT, but I want to suppress the second f2 element in the output - how do I check inside my template to see if the f2 element already exists in the output when the second f2 element in the source is processed? My XSLT looks something like this at present:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output method="xml" indent="no" omit-xml-declaration="yes" standalone="no" />
  <xsl:template match="/">
    <xsl:for-each select="./root">
      <output>
        <xsl:apply-templates />        
      </output>
    </xsl:for-each>
  </xsl:template>
  <xsl:template match="*" >
      <xsl:element name="{name(.)}">
        <xsl:value-of select="." />
      </xsl:element>
  </xsl:template>
</xsl:stylesheet>

I need to do some sort of check around the xsl:element in the template I think, but I'm not sure how to interrogate the output document to see if the element is already present.

Edit: Forgot the pre tags, code should be visible now!

Was it helpful?

Solution

It depends how system wide you want to be.

i.e. Are you only concerned with elements that are children of the same parent, or all elements at the same level ('cousins' if you like) or elements anywhere in the document...

In the first situation you could check the preceding-sibling axis to see if any other elements exist with the same name.

<xsl:if test="count(preceding-sibling::node()[name()=name(current())])=0">
  ... do stuff in here.
</xsl:if>

OTHER TIPS

To only check (and warn you of a duplicate), you may find an example here

Something along the lines of:

<xsl:for-each-group select="collection(...)//@id" group-by=".">
  <xsl:if test="count(current-group()) ne 1">
    <xsl:message>Id value <xsl:value-of select="current-grouping-key()"/> is 
       duplicated in files
       <xsl:value-of select="current-group()/document-uri(/)" separator=" and
    "/></xsl:message>
  </xsl:if>
 </xsl:for-each-group>

To be modified to select all nodes within 'root' element.

As to remove the duplicate lines, you have another example here

That would look like:

<xsl:stylesheet>
  <xsl:key name="xyz" match="record[x/y/z]" use="x/y/z" />
  <xsl:variable name="noxyzdups" select="/path/to/record[generate-id(.) = generate-id(key('xyz', x/y/z))]" />
...
  <xsl:template ... >
    <xsl:copy-of "exslt:node-set($noxyzdups)" />
  </xsl:template>
</xsl:stylesheet>

x/y/z is the xpath expression that you want made unique. It can be concat(x,'-',@y,'-',z) or whatever you want.

Now I am not sure those two examples can easily be adapted to your case, but I just wanted to point out those two sources, in case it helps.

It's not possible to interrogate the output of your transform. It's also not possible to track the current state of your transform (i.e. keep track of what nodes you've emitted in a variable). Fundamentally, that's not how XSLT works. One of the costs of a side-effect-free programming environment is that you can't do things that have side effects. Oh well.

In your case, one way of accomplishing this would be to build a variable that contained a list of all of the source elements that could be transformed into the output element that you want to emit only once. Then check every node you're transforming against this list. If it's not in the list, emit it. If it's the first item in the list, emit it. Otherwise, don't.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top