计算先前的节点会返回意外结果

时间:2016-12-07 12:32:22

标签: xslt xslt-2.0 saxon

我想使用逐项列表而不是硬中断,请参阅示例:

<para>line1<?linebreak?>
line2<?linebreak?>
line3</para>

但是,我在递归模板中遇到奇怪的行为,导致无法正确处理第二行。我已经创建了简化的测试用例 - 不再是递归的了。如果以这种方式使用count(preceding::processing-instruction('linebreak')) = 0表达式,则不返回任何内容,但我希望第二行。

<line>line1</line><node>
line2<?linebreak?>
line3</node>
line2

<node>元素用于调试目的。它确认我处理了预期的数据。

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">

    <xsl:template match="para[processing-instruction('linebreak')]">
        <xsl:call-template name="getLine">
            <xsl:with-param name="node" select="./node()"/>
        </xsl:call-template>
    </xsl:template>

    <xsl:template name="getLine">
        <xsl:param name="node"/>

        <line>
            <xsl:copy-of
                select="$node/self::processing-instruction('linebreak')[not(preceding::processing-instruction('linebreak'))]/preceding::node()"
            />
        </line>

        <xsl:call-template name="getSecondLine">
            <xsl:with-param name="node"
                select="$node/self::processing-instruction('linebreak')[not(preceding::processing-instruction('linebreak'))]/following::node()"
            />
        </xsl:call-template>

    </xsl:template>

    <xsl:template name="getSecondLine">
        <xsl:param name="node"/>

        <node>
            <xsl:copy-of select="$node"/>
        </node>

        <xsl:copy-of
            select="$node/self::processing-instruction('linebreak')[count(preceding::processing-instruction('linebreak')) = 0]/preceding::node()"
        />
    </xsl:template>

</xsl:stylesheet>

在Saxon HE / EE 9.6.0.7中测试(在Oxygen XML Editor 18中)。

1 个答案:

答案 0 :(得分:2)

第一个换行符的处理工作正常:

<line>
   <xsl:copy-of
     select="$node/self::processing-instruction('linebreak')
              [not(preceding::processing-instruction('linebreak'))]
              /preceding::node()"/>
</line>

虽然只在这个样本上;对于更复杂的数据,您会得到错误的结果,因为您应该使用preceding-sibling轴而不是preceding轴。

但代码可以大大简化,我会将select表达式写为:

select="$node[self::processing-instruction('linebreak')][1]
        /preceding-sibling::node()"

第二次换行的处理似乎很困惑。您正在传递参数

$node/self::processing-instruction('linebreak')
   [not(preceding::processing-instruction('linebreak'))]
   /following::node()"

实际上是

select="$node[self::processing-instruction('linebreak')][1]
            /following-sibling::node()"

选择三个节点

line2<?linebreak?>line3

(加上空白),您在<node>元素中输出,生成

<node>line2<?linebreak?>line3</node>

(再次忽略空白)

然后你做

select="$node/self::processing-instruction('linebreak')
  [count(preceding::processing-instruction('linebreak'))=0]
  /preceding::node()"

这里$node/self::processing-instruction('linebreak')选择这三个节点中的第二个节点,这是第二个换行处理指令。前面(或前面的兄弟)处理指令的计数是1,因为你正在处理的指令是第二个。

我不太清楚你在想什么,但我怀疑你的错误是将“前面”和“跟随”视为相对于$node序列中节点位置的选择,而不是相对于原始源树中的其他节点。我建议阅读描述各种轴的XPath参考书的部分。