I have the following template:
<xsl:template match="footnote">
<xsl:variable name = "string">
<xsl:value-of select="."/>
</xsl:variable>
<xsl:variable name = "bool">
<xsl:if test="$string = preceding-sibling::node()/$string">
<xsl:text>false</xsl:test>
</xsl:if>
</xsl:variable>
<xsl:if test="$bool != 'false'">
<!-- DO STUFF -->
</xsl:if>
</xsl:template>
I'm trying to check the $string variable of the current node and check it against all previous footnote nodes to see if they have the same $string variable. If it doesn't match up with any of the preceding siblings then it should do stuff, otherwise it should do nothing.
With the code I have, the test "$string = preceding-sibling::node()/$string" always evaluates to true, even when no footnote nodes have been created yet.
Can anyone help me out? I'm having a hard time creating the expression to compare against the variable in all of the previous siblings.
EDIT: Sample XML:
<xml>
<footnote>Footnote 1</footnote>
<footnote>Footnote 2</footnote>
<footnote>Footnote 1</footnote>
<footnote>Footnote 1</footnote>
<footnore>Footnote 3</footenote>
</xml>
I'm trying to transform that into:
<xml>
<footnote>Footnote 1</footnote>
<footnote>Footnote 2</footnote>
<footnote>Footnore 3</footnore>
</xml
Well. posting Your output XML made it more clear! there you go with solution:
Sample XML:
<?xml version="1.0" encoding="utf-8"?>
<root>
<footnote>Matching</footnote>
<footnote>Example1</footnote>
<footnote>Matching</footnote>
<footnote>Example2</footnote>
<footnote>Example3</footnote>
</root>
And XSL:
<?xml version="1.0" encoding="utf-8"?>
<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="footnote[.=preceding-sibling::footnote/.]"/>
</xsl:stylesheet>
Result:
<?xml version="1.0" encoding="utf-8"?>
<xml>
<footnote>Matching</footnote>
<footnote>Example1</footnote>
<footnote>Example2</footnote>
<footnote>Example3</footnote>
</xml>
Assuming you want to check the value of the current footnote text()
against previous footnote text()'s
for uniqueness, then you can just check the preceding-sibling::node()'s text()
:
<xsl:if test="$string = preceding-sibling::node()/text()">
e.g.
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0"
>
<xsl:output omit-xml-declaration="yes" method="xml" version="1.0" indent="yes" />
<xsl:template match="/xml">
<xml>
<xsl:apply-templates select="footnote" />
</xml>
</xsl:template>
<xsl:template match="footnote">
<xsl:variable name = "string">
<xsl:value-of select="./text()"/>
</xsl:variable>
<xsl:variable name = "bool">
<xsl:if test="$string = preceding-sibling::node()/text()">
<xsl:text>false</xsl:text>
</xsl:if>
</xsl:variable>
<xsl:if test="$bool != 'false'">
<xsl:copy-of select="."></xsl:copy-of>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
Will turn the input:
<xml>
<footnote>
someVal
</footnote>
<footnote>
anotherVal
</footnote>
<footnote>
anotherVal
</footnote>
<footnote>
newVal
</footnote>
</xml>
Into:
<xml>
<footnote>
someVal
</footnote>
<footnote>
anotherVal
</footnote>
<footnote>
newVal
</footnote>
</xml>
You should also look at Muenchian grouping if your intention is to identify groups within elements.
You could use
<xsl:template match="footnote[not(. = preceding-sibling::footnote)]">
to create a template that only fires for the first instance of a given footnote string. The . = preceding-sibling::footnote
is true if the string value of this node is the same as that of any of its preceding siblings, so not(....)
will be true if it is different from all of them.
But for large documents this will be rather inefficient (quadratic in the number of footnote elements). Better would be to use <xsl:for-each-group>
(XSLT 2.0) or Muenchian Grouping (XSLT 1.0) to group the footnote
elements by their string value and extract the first element from each group.
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:key name="fn" match="footnote" use="." />
<!-- Ignore all but the first footnote for a given value -->
<xsl:template match="footnote[generate-id() != generate-id(key('fn', .)[1])]" />
<!-- copy everything else verbatim -->
<xsl:template match="@* | node()">
<xsl:copy>
<xsl:apply-templates select="@* | node()"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
<xsl:template match="footnote">
<xsl:variable name = "string">
<xsl:value-of select="./text()"/>
</xsl:variable>
<xsl:variable name = "bool">
<xsl:if test="$string != preceding-sibling::node()/text()">
<xsl:text>false</xsl:text>
</xsl:if>
</xsl:variable>
<xsl:if test="$bool != 'false'">
true
</xsl:if>
<xsl:if test="$bool = 'false'">
false
</xsl:if>
</xsl:template>
In XSLT 2.0 you have a distinct-values function:
<xsl:for-each select="distinct-values(footnote)">
<footnote><xsl:value-of select="."/></footnote>
</xsl:for-each>