【问题标题】:unable to get the node using xpath which reads the desired ID from the input无法使用从输入中读取所需 ID 的 xpath 获取节点
【发布时间】:2017-09-22 17:12:00
【问题描述】:

我试图通过动态创建 xpath 来获取 xml 节点的属性值。但它一直在返回空值。如果我对 xpath 进行硬编码,它可以正常工作。下面是一个示例 xml 文件:

<root>  
<PR id="id6016" name="OUTER WORLD">
    <ADS id="id6017" dsRef="#id15" role="form1">
    </ADS>
    <ADS id="id6018" dsRef="#id9" role="form1">
    </ADS>
</PR>

<PR id="id1000" name="OUTER WORLD">
    <ADS id="id1001" dsRef="#id16" role="form1">
    </ADS>
    <ADS id="id1002" dsRef="#id10" role="form1">
    </ADS>
</PR>

<DS id="id9" name="form1" version="7" type="CAD">
</DS>

<DS id="id15" name="form1" version="1" type="MSWord">
</DS>

<DS id="id10" name="form1" version="1" type="CAD">
</DS>

<DS id="id16" name="form1" version="1" type="MSWord">
</DS>

</root>

我的示例 xsl 是:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output method="xml" omit-xml-declaration="no" indent="no" encoding="utf-8"/>
  <xsl:template match="/root">
    <result>
      <xsl:apply-templates select="PR" mode="PR" />
    </result>
  </xsl:template>

  <xsl:template match="PR" mode="PR">
    <PR>
      <xsl:attribute name="id">
        <xsl:value-of select="@id" />
      </xsl:attribute>
      <xsl:attribute name="name">
        <xsl:value-of select="@name" />
      </xsl:attribute>
      <xsl:for-each select="ADS[@role='form1']">
        <DS>
          <xsl:attribute name="id">
            <xsl:value-of select="string(substring-after(@dsRef, '#'))" />
          </xsl:attribute>
          <xsl:value-of select="../DS[@id=string(substring-after(@dsRef, '#'))]/@type" />
        </DS>           

      </xsl:for-each>
      <DS2>
        <xsl:value-of select="../DS[@id='id9']/@type" />
      </DS2>      
      <DS3>
        <xsl:value-of select="../DS[@id=string(substring-after(@dsRef, '#'))]/@type" />
      </DS3>

    </PR>
  </xsl:template>
</xsl:stylesheet>

如您所见,我得到了 DS2 标记的任何内部文本,但 DS 或 DS3 没有。我还在 DS 标记中添加了 id 属性,以表明我得到的 id 是好的。

最终我想过滤具有 type="CAD" 的 DS 标记并将其 id 存储在一个变量中,但现在我无法使用 xpath 获取 xml 节点,所以我被卡住了。

【问题讨论】:

    标签: xml xslt xpath xslt-1.0


    【解决方案1】:

    如果我正确理解了您对问题的描述,说明

    <xsl:value-of select="../DS[@id='id9']/@type" />
    

    在 DS2 元素中产生期望值和期望值,但指令

    <xsl:value-of select="../DS[@id=string(substring-after(@dsRef, '#'))]/@type" />
    

    在 DS3 元素中不是。同样,在 xsl:for-each 中生成的 DS 元素上的 @id 属性带有预期值,但元素内容与预期不符。 (专业提示:如果不那么繁琐地了解您得到的结果与您的预期和想要的结果有何不同,那么您会更幸运地在 Stack Overflow 上获得问题的答案。)

    这里有两个问题。

    首先,对@dsRef 的引用在xsl:for-each 的第一个xsl:value-of 中成功,因为该属性引用的上下文元素是带有dsRef 元素的ADS 元素。 xsl:for-each 的第二个 xsl:value-of 中对 @dsRef 的引用失败,因为它位于谓词内,并且上下文元素是正在测试谓词的 DS 元素。在 DS3 调试元素中对 @dsRef 的引用与对 DS 元素上不存在的“dsRef”属性的引用类似。

    有两种方法可以解决这个问题。您可以阅读current(),其中包括一种从谓词内指向整个XPath 表达式的上下文元素的方法,并将属性引用重写为current()/@dsRef 之类的东西。很多人会推荐这条路。

    或者您可以避免必须了解current() 以及它在任何给定点爬升上下文节点堆栈的确切步骤,并将您想要的值绑定到您可以在表达式中引用的变量。 (我从经验中知道,有可能花费数年时间编写 XSLT 程序,而永远不需要了解 current() 的所有繁琐细节。)在这种情况下,您的 for-each 看起来像这样:

    <xsl:for-each select="ADS[@role='form1']">
      <xsl:variable name="targetDS" select="substring-after(@dsRef,'#')"/>
      <DS>
        <xsl:attribute name="id">
          <xsl:value-of select="$targetDS" />
        </xsl:attribute>
        <xsl:value-of select="../DS[@id=$targetDS]/@type" />
      </DS>           
    </xsl:for-each>
    

    您可能会发现(和我一样)在命名变量中包含目标 ID 可以更容易地理解两个 value-of 指令的作用。

    但是,在此更改之后,您的程序仍然无法运行。

    您的第二个问题是您正在当前 ADS 元素的兄弟姐妹中寻找 DS 元素,但您输入的 ADS 元素实际上没有 DS 兄弟;他们都是PR的兄弟姐妹。如果元素上的 'id' 属性表现为 XML ID,则元素的层次位置无关紧要,而是更简单的表达式,如

    //DS[@id=$targetDS]
    

    会更有弹性(也更清晰)。如果你有一个 DTD 并且 'id' 属性被声明为具有类型 ID,那么写起来就更简单了

    id($targetDS)
    

    表示“ID 为 $targetDS 的元素”。如果出于某种原因您确实希望将表达式限制为输入文档特定区域中的 DS 元素,则需要确保正确引用该区域。在我将.. 替换为../.. 后,样式表产生了我认为的预期结果。

    【讨论】:

    • 感谢您的解释。我没有使用 current() 函数,但我会考虑在我的代码中更频繁地使用它。我上面分享的xml只是一个sn-p,不幸的是原始xml不包含定义ID的DTD。我不确定我是否遵循您将 .. 替换为 ../.. 的解决方案,当我在 xpath 中对 id9 进行硬编码时,我得到了 type 属性的值,但在使用变量时却没有。
    • 您的两个调试元素 DS2 和 DS3 在 for-each 之外:上下文节点是 PR,而不是 ADS。如果将它们移动到 for-each 中,使上下文节点成为 ADS 元素,您将看到两者都失败了。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-27
    • 1970-01-01
    • 2016-01-09
    • 2014-08-17
    • 1970-01-01
    • 2023-04-01
    相关资源
    最近更新 更多