XSLT匹配节点不值

时间:2013-09-05 17:56:16

标签: xslt xslt-2.0 dita

以下XSLT片段用于包装/标记具有给定@audience属性的第一个和最后一个文本节点以进行突出显示。

<xsl:template match="text()">
    <xsl:if test=". = ((((ancestor::*[contains(@audience,'FLAG_')])[last()])/descendant::text())[1])">                  
        <xsl:call-template name="flagText"/>            
    </xsl:if>
    <xsl-value-of select="."/>
    <xsl:if test=". = ((((ancestor::*[contains(@audience,'FLAG_')])[last()])/descendant::text())[last()])">                 
        <xsl:call-template name="flagText"/>            
    </xsl:if>
</xsl:template>

伪代码
找到与标志条件匹配的最后(最近的)祖先元素,然后找到作为该元素后代的第一个和最后一个文本节点并标记它们。

逻辑正确但执行错误。这确实找到了第一个和最后一个文本节点,但它匹配的是值而不是节点。这标记了与第一个或最后一个节点具有相同值的任何文本节点。

示例
The quick brown fox jumped over the lazy dog.

电流输出:
[FLAG]The quick brown fox jumped over [FLAG]the lazy dog[FLAG].

[1]和狗[last()]被正确标记,但由于字符串匹配或等于第一个,它也在中间捕捉'the'这个词。

编辑:
预期(期望)输出:
[FLAG]The quick brown fox jumped over the lazy dog.[FLAG]

如何重新组织我的语句以仅匹配第一个和最后一个节点?我不想比较我想要选择第一个和最后一个的字符串。

编辑:

示例源XML

<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE concept PUBLIC "-//OASIS//DTD DITA Concept//EN" "concept.dtd">
<concept audience="Users" id="concept_lsy_5vg_kl"><title>Product ABC</title><conbody><p>This is a blurb about <ph>Product ABC</ph>. Since the text in the phrase (ph) matches the text node in the title (first text node) it will be flagged. I only want the first and last nodes flagged. Basically, I don't want to compare the contents of the nodes. <ph audience="Users">I also need to support inline cases such as this one. </ph>I just want the flags before and after the first and last text nodes for each audience match.</p></conbody></concept>

示例XSLT

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

    <xsl:output method="text" omit-xml-declaration="yes" indent="yes"/>

    <xsl:template match="text()">
        <xsl:if test=". = ((((ancestor::*[contains(@audience,'Users')])[last()])/descendant::text())[1])">          
            <xsl:text>[USERS]</xsl:text>            
        </xsl:if>
        <xsl:value-of select="."/>
        <xsl:if test=". = ((((ancestor::*[contains(@audience,'Users')])[last()])/descendant::text())[last()])">         
            <xsl:text>[/USERS]</xsl:text>           
        </xsl:if>
    </xsl:template> 



</xsl:stylesheet>

当前输出

[USERS]产品ABCT这是关于[USERS]产品ABC的简介。由于短语(ph)中的文本与标题(第一个文本节点)中的文本节点匹配,因此将标记该文本。我只希望标记第一个和最后一个节点。基本上,我不想比较节点的内容。 [用户]我还需要支持这样的内联案例。 [/ USERS]我只想在每个观众匹配的第一个和最后一个文本节点之前和之后的标志。[/ USERS]

所需的输出 [用户]产品ABCT这是关于产品ABC的简介。由于短语(ph)中的文本与标题(第一个文本节点)中的文本节点匹配,因此将标记该文本。我只希望标记第一个和最后一个节点。基本上,我不想比较节点的内容。 [用户]我还需要支持这样的内联案例。 [/ USERS]我只想在每个观众匹配的第一个和最后一个文本节点之前和之后的标志。[/ USERS]

感谢。

2 个答案:

答案 0 :(得分:1)

请原谅基本问题,但我不明白为什么你只是在@audience设置为用户时不使用ditaval文件突出显示文本?您可以基本上为文本着色,以便您的审阅者可以找到文本并进行验证。审核后,您可以关闭突出显示。

也许我误解了你的意图。

答案 1 :(得分:1)

在您的表达式中,您将获得您所在文本节点的“用户”类名称的“最后”祖先

...(ancestor::*[contains(@audience,'FLAG_')])[last()])...

但你真的想在这里得到第一个。也就是说,最直接的祖先。祖先列表将从父节点开始,然后是祖父节点等......

...(ancestor::*[contains(@audience,'FLAG_')])[1])...

此外,你说你不想比较字符串,只是找出文本节点是第一个还是最后一个,但你当前的比较是检查字符串。因此,如果您有两个文本节点具有相同的文本(其中一个是第一个),那么您将获得一个流氓'[USER]'标记。要测试两个节点是否是同一节点(而不是仅具有相同的内容),可以使用generate-id函数。

generate-id() = generate-id(((ancestor::*[contains(@audience,'Users')][1])/descendant::text())[1])

试试这个XSLT

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0" >
  <xsl:output method="text" omit-xml-declaration="yes" indent="yes"/>

  <xsl:template match="text()">
    <xsl:if test="generate-id() = generate-id(((ancestor::*[contains(@audience,'Users')][1])/descendant::text())[1])">
      <xsl:text>[USERS]</xsl:text>
    </xsl:if>
    <xsl:value-of select="."/>
    <xsl:if test="generate-id() = generate-id(((ancestor::*[contains(@audience,'Users')][1])/descendant::text())[last()])">
      <xsl:text>[/USERS]</xsl:text>
    </xsl:if>
  </xsl:template>
</xsl:stylesheet>

但是......也许你可以采取另一种方法。纯粹看你的XML和预期输出,可以采取的另一种方法是使模板与任何元素匹配'users'类,然后用 xsl:apply-templates 以选择所有文字。

尝试使用此XSLT作为替代方案:

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0" >
  <xsl:output method="text" omit-xml-declaration="yes" indent="yes"/>

  <xsl:template match="*[contains(@audience,'Users')]">
    <xsl:text>[USERS]</xsl:text>
      <xsl:apply-templates />
    <xsl:text>[/USERS]</xsl:text>
  </xsl:template>
</xsl:stylesheet>