无法使用xpath从输入中读取所需的ID

时间:2017-09-22 17:12:00

标签: xml xslt xpath xslt-1.0

我试图通过动态创建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是好的。

最终我想过滤类型为&#34; CAD&#34;的DS标签。并将它的id存储在一个变量中,但是现在我无法使用xpath获取xml节点,所以我被卡住了。

1 个答案:

答案 0 :(得分:0)

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

<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中成功,因为该属性引用的context元素是带有dsRef元素的ADS元素。在xsl:for-each的第二个xsl:value-of中对@dsRef的引用失败,因为它在谓词中,而context元素是在其上测试谓词的DS元素。 DS3调试元素中对@dsRef的引用同样是对不存在的“dsRef”的引用。 DS元素的属性。

有两种方法可以解决这个问题。您可以阅读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可以更容易理解两个指令值的作用。

但是,在此更改之后,您的程序仍无效。

您的第二个问题是您正在寻找当前ADS元素的兄弟姐妹中的DS元素,但您输入中的ADS元素实际上没有DS兄弟;他们都是公关的兄弟姐妹。如果&#39; id&#39;元素上的属性表现为XML ID,然后元素的层次结构位置并不重要,而且表达式更简单,如

//DS[@id=$targetDS]

会更有弹性(更清晰)。如果你有一个DTD和&#39; id&#39;属性被声明为具有类型ID,写入

更简单
id($targetDS)

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