【发布时间】:2021-10-31 17:29:28
【问题描述】:
我在我的 xml 文件中使用 XSLT 1.0。对于其中一个更改,我必须先将节点值的子字符串替换为另一个字符串,然后对其调用key()。
假设节点值为'alpha.beta',那么在使用str:replace() 之后按照thread 的建议,值更新为'alpha.gamma'。稍后我在其上调用key(),对于key() 中实际存在节点的情况返回false。
我尝试的示例让我怀疑 replace() 是否正在更改元素是有道理的:
Case1:
pass the string 'alpha.gamma' directly to the key()
key(some-defined-key, 'alpha.gamma') returns true
Case2:
update the variable var from 'alpha.beta' to 'alpha.gamma' and pass the variable
key(some-defined-key, $var) returns false
我在每一步都打印了节点的值,以确保节点的值得到正确更新。所以,
- xslt 1.0 中的 key() 函数是否存在某种缺陷?
- replace() 改变节点是否有某种可能性是有道理的?
- 有没有办法查明原因?
更新:
我试图对确切的场景进行原型设计,请多多包涵。首先,我有一个 xsl 文件,其中包含在 local-namespace 的帮助下存储的我想吃的水果列表:
本地命名空间.xsl:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet
version="1.0"
xmlns:lcl="my_local_namespace"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<lcl:fruits>
<lcl:fruit>apple</lcl:fruit>
<lcl:fruit>banana</lcl:fruit>
</lcl:fruits>
<xsl:variable name="fruits-list" select="document('')/xsl:stylesheet/lcl:fruits/lcl:fruit"/>
</xsl:stylesheet>
现在我有一个显示可用水果的 xml 文件,我们称之为 fruits.xml:
<fruits>
<fruit-available>
<fruit name="fresh-apple"></fruit>
<fruit name="fresh-banana"></fruit>
</fruit-available>
<fruit-selector>fresh-${FRUIT-NAME}</fruit-selector>
</fruits>
现在,使用 fruit-selector 节点的值,我必须用我想吃的每个水果(即 lcl:fruit 元素)替换 ${FRUIT-NAME},如果 fruit-available 节点中不存在水果,那么生成一个错误标签,指出特定水果不可用。
fruits.xsl 文件:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet
version="1.0"
xmlns:lcl="my_local_namespace"
xmlns:str="http://exslt.org/strings"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:import href="local-namespace.xsl"/>
<xsl:import href="http://exslt.org/str/functions/replace/str.replace.function.xsl"/>
<xsl:key name="fruitsKey" match="fruits/fruit-available/fruit" use="@name"/>
<xsl:output indent="yes"/>
<xsl:template match="text()"/>
<xsl:template match="/">
<Errors>
<xsl:apply-templates/>
</Errors>
</xsl:template>
<xsl:template name="CheckFruits">
<xsl:param name="fruitElement"/>
<xsl:param name="currentFruit"/>
<xsl:variable name="selector" select="str:replace($fruitElement, '${FRUIT-NAME}', string($currentFruit))"/>
<xsl:if test="not(key('fruitsKey', $selector))">
<Error><xsl:value-of select="concat('Fruit element: ', $selector, ' is not available')"/></Error>
</xsl:if>
</xsl:template>
<xsl:template match="fruits/fruit-selector">
<xsl:variable name ="currentfruitElement" select="."/>
<xsl:for-each select="$fruits-list">
<xsl:call-template name="CheckFruits">
<xsl:with-param name="fruitElement" select="$currentfruitElement"/>
<xsl:with-param name="currentFruit" select="."/>
</xsl:call-template>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
我怀疑key() 即使在有效的情况下也显示为假:
执行结果:
xsltproc --xinclude fruits.xsl fruits.xml
<?xml version="1.0"?>
<Errors xmlns:lcl="my_local_namespace" xmlns:str="http://exslt.org/strings">
<Error>Fruit element: fresh-apple is not available</Error>
<Error>Fruit element: fresh-banana is not available</Error>
</Errors>
注意: 我是一个初学者,所以即使错误很简单,我也有兴趣理解所涉及的概念。
【问题讨论】:
-
请同时显示一个最小但完整的 XSLT 示例,以说明问题。一个键建立在一个输入文档上,任何中间结果在 XSLT 1.0 中首先是一个结果树片段。您可以将其转换为节点集,但它将是与输入文档不同的文档。我们确实需要了解您的密钥是如何声明的、如何应用更改以及在何处/如何使用密钥功能。
-
如果不提供重现问题的方法,您只能期望猜测。我的猜测是,简单的解决方案是颠倒操作顺序:首先使用密钥获取您想要的节点,然后替换您想要替换的部分。 -- 在任何情况下,XSL 转换都对输入树进行操作——并且该树永远不会被转换修改。转换所做的只是将结果写入输出树。
-
这不是“展示问题的最小但完整的 XSLT 示例”。
-
附注:EXSLT str:replace() 函数的规范声明结果应该是节点集。我已经在exslt.org/str/functions/replace/str.replace.function.xsl 测试了实现,它确实符合。
-
我们还需要知道您使用的是什么 XSLT 处理器。您正在调用的
str:replace()的实现调用func:function,它是规范的扩展,在某些处理器中的实现可能与在其他处理器中不同。通常,您在这里非常依赖处理器 - 如果您迁移到 XSLT 2.0,事情会更清楚。