Compare variable in preceding-sibling with current

2019-08-02 03:39发布

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

标签: xslt
5条回答
小情绪 Triste *
2楼-- · 2019-08-02 04:15

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>
查看更多
够拽才男人
3楼-- · 2019-08-02 04:22

<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>

查看更多
Rolldiameter
4楼-- · 2019-08-02 04:29

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>
查看更多
趁早两清
5楼-- · 2019-08-02 04:31

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.

查看更多
倾城 Initia
6楼-- · 2019-08-02 04:36

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>
查看更多
登录 后发表回答