I've looked high and low for an answer to this, and tried hundreds of permutations, but nothing at all has worked.
I'm trying to process all the nodes of a simple XML document EXCEPT for the very first <title>
node. Basically, I'm trying to find an xslt instruction that accomplishes the exact INVERSE of
<xsl:apply-templates select="/topic/title"/>
Here is my source XML:
<topic>
<title>Name of the Document</title>
<p>Document body</p>
<topic>
<title>First Document Subtopic</title>
<p>Body text for first document subtopic</p>
</topic>
<p>Body text continued for document</p>
<topic>
<title>Second Document Subtopic</title>
<p>Body text for document subtopic</p>
<topic>
<title>First Document Sub-subtopic</title>
<p>Body text for first document sub-subtopic</p>
</topic>
</topic>
<p>Body text continued a second time for document</p>
</topic>
and here is my XSLT (with what I originally thought should work in the call to apply-templates):
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
<xsl:output omit-xml-declaration="yes" encoding="UTF-8" method="xml"/>
<xsl:strip-space elements="*"/>
<xsl:template match="/">
<BOX>
<TEXTFLOW>
<xsl:apply-templates select="*[not(/topic/title)]"/>
</TEXTFLOW>
</BOX>
</xsl:template>
<xsl:template match="topic">
<xsl:apply-templates/>
</xsl:template>
<xsl:template match="title">
<PARA STYLE="Title"/>
<xsl:apply-templates/>
</xsl:template>
<xsl:template match="p">
<PARA STYLE="BodyText"/>
<xsl:apply-templates/>
</xsl:template>
<xsl:template match="text()">
<FORMAT>
<xsl:value-of select="."/>
</FORMAT>
</xsl:template>
</xsl:stylesheet>
and here's what I want to see (note the absence of the first <title>
matter):
<BOX>
<TEXTFLOW>
<PARA STYLE="BodyText"/>
<FORMAT>Document body</FORMAT>
<PARA STYLE="Title"/>
<FORMAT>First Document Subtopic</FORMAT>
<PARA STYLE="BodyText"/>
<FORMAT>Body text for first document subtopic</FORMAT>
<PARA STYLE="BodyText"/>
<FORMAT>Body text continued for document</FORMAT>
<PARA STYLE="Title"/>
<FORMAT>Second Document Subtopic</FORMAT>
<PARA STYLE="BodyText"/>
<FORMAT>Body text for document subtopic</FORMAT>
<PARA STYLE="Title"/>
<FORMAT>First Document Sub-subtopic</FORMAT>
<PARA STYLE="BodyText"/>
<FORMAT>Body text for first document sub-subtopic</FORMAT>
<PARA STYLE="BodyText"/>
<FORMAT>Body text continued a second time for document</FORMAT>
</TEXTFLOW>
</BOX>
What select expression can I use to accomplish this?
Well, you can just use this XPath in your root template:
select=".//*[generate-id() != generate-id(/topic/title)]"
You'd need to change your template for topic
to:
<xsl:template match="topic" />
The problem you're having is because your current select only specifies the immediate children that should be processed- from the root note (/
), this is actually only the top level topic
element. The .//*
in the XPath above specifies all descendants regardless of level, and the predicate explicitly excludes the first /topic/title
element.
You need to make sure your topic
elements are NOT processing their children, as their children are already being processed by the select instruction in the root template.