我试图通过动态创建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标签有任何Innertext,但对于DS或DS3而言。我还将ID属性添加到DS标签中,以表明我获得的ID很好。
最终我想过滤具有type =" cad"并将其ID存储在变量中的DS标签,但是现在我无法使用XPath获取XML节点,因此我被卡住了。
如果我正确理解了您对问题的描述,则指令
<xsl:value-of select="../DS[@id='id9']/@type" />
在DS2元素中正在产生预期和所需的值,但是指令
<xsl:value-of select="../DS[@id=string(substring-after(@dsRef, '#'))]/@type" />
在DS3元素中不是。同样,在XSL中生成的DS元素上的@ID属性:for-eht具有期望值,但是元素内容不如预期。(专家提示:如果不是很琐事,您将有更好的运气引起堆栈溢出问题的答案
您在这里有两个问题。
首先,对@dsRef
的引用在第一个XSL:XSL:for-EAST的XSL:foration中成功,因为该属性引用的上下文元素是带有dsRef
元素的ADS元素。第二个XSL中对@dsRef
的引用:XSL的值:for-east失败,因为它在谓词内部,并且上下文元素是对谓词进行测试的DS元素。DS3调试元素中对@dsRef
的引用同样是对DS元素上不存在的" DSREF"属性的引用。
有两种解决此问题的方法。您可以在current()
上阅读,其中包括从谓词内部指向整个XPATH表达式的上下文元素的一种方式,并将属性引用重写为类似于current()/@dsRef
的内容。许多人会推荐这条道路。
或者您可以避免了解current()
,以及在任何给定点上爬上的上下文节点的堆栈,并将您想要的值绑定到可以在表达式中引用的变量。(我从经验中知道,可以花费数年的时间编写XSLT程序,而不需要理解current()
的所有细节。)在这种情况下,您的for-EATH会看起来像这样:
<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使得更容易理解两个指令正在做什么。
此更改后,您的程序仍然不起作用。
您的第二个问题是您正在寻找当前广告元素的兄弟姐妹之间的DS元素,但是输入中的ADS元素实际上没有DS兄弟姐妹。他们都是PR的兄弟姐妹。如果您的元素上的" ID"属性以XML ID的形式行为,则该元素的层次位置是无关紧要的,并且更简单的表达式(例如
)//DS[@id=$targetDS]
将更具弹性(更清晰)。如果您有DTD,并且" ID"属性被声明为具有类型ID,那么编写
甚至更简单id($targetDS)
这意味着" ID为$ targets的元素"。如果由于某种原因您真的想将表达式限制为输入文档特定区域中的DS元素,则需要确保正确地参考该区域。我用../..
替换..
后,样式表产生了我认为的预期结果。