XSL 从另一个文档评估动态 XPATH
Posted
技术标签:
【中文标题】XSL 从另一个文档评估动态 XPATH【英文标题】:XSL evaluate dymanic XPATH from another document 【发布时间】:2021-12-23 18:11:46 【问题描述】:我在下面定义了两个示例文档。在 module_meta.xml
中,只有 xpath /mdata/effectivity
上的效果节点是相关的。如下所示,它们包含一个path
属性和一个effrg
属性。现在的目标是评估module.xml
上的xpath(在module_meta.xml
中定义为path
属性)并将effrg
附加到它上面。有关所需结果,请参阅desired_output.xml
。 xsl 转换应用于module.xml
。我知道我必须使用document()
函数来“包含”module_meta.xml
,但到目前为止我很茫然。
module.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE proc>
<procbody>
<info>
<action lid="a">
</action>
<action lid="b">
</action>
<action lid="c">
</action>
</info>
</procbody>
module_meta.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mdata>
<mdata>
<metadata>
<metadata-item name="n1" value="v1" />
<metadata-item name="n2" value="v2" />
<metadata-item name="n3" value="v3" />
</metadata>
<effectivity>
<effect path="//*[@lid='a']" effrg="0074 0080 0087" />
<effect path="//*[@lid='b']" effrg="0136 0146 0174" />
</effectivity>
</mdata>
desired_output.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE proc>
<procbody>
<info>
<action lid="a" effrg="0074 0080 0087">
</action>
<action lid="b" effrg="0136 0146 0174">
</action>
<action lid="c">
</action>
</info>
</procbody>
【问题讨论】:
XSLT 3(受 Saxon 10 及更高版本的所有版本、Saxon 9.8 及更高版本的 PE 和 EE、Saxon-JS 2 和 Altova XML 2017 R3 及更高版本的支持)具有xsl:evaluate
。一些较旧的实现提供扩展功能或让您设置一个用于 XPath 评估。或者你可以链接两个样式表。
【参考方案1】:
在支持 xsl:evaluate
的 XSLT 3 中:
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="3.0"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="#all"
xmlns:mf="http://example.com/mf"
expand-text="yes">
<xsl:function name="mf:evaluate" as="element(action)?">
<xsl:param name="effect" as="element(effect)"/>
<xsl:evaluate xpath="$effect/@path" context-item="$main-doc"/>
</xsl:function>
<xsl:variable name="main-doc" select="/"/>
<xsl:mode on-no-match="shallow-copy"/>
<xsl:template match="info/action">
<xsl:copy>
<xsl:apply-templates select="@*, $meta//effect[current() is mf:evaluate(.)]/@effrg"/>
</xsl:copy>
</xsl:template>
<xsl:param name="meta">
<mdata>
<metadata>
<metadata-item name="n1" value="v1" />
<metadata-item name="n2" value="v2" />
<metadata-item name="n3" value="v3" />
</metadata>
<effectivity>
<effect path="//*[@lid='a']" effrg="0074 0080 0087" />
<effect path="//*[@lid='b']" effrg="0136 0146 0174" />
</effectivity>
</mdata>
</xsl:param>
</xsl:stylesheet>
为了示例的自包含性,第二个文档是作为参数内联的,但您当然可以改用<xsl:param name="meta" select="doc('module_meta.xml')"/>
。
对于每个effect
元素只使用一次xsl:evaluate
可能会更有效,例如,通过声明一个这样做的键,并存储生成的ID(如果有):
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="3.0"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="#all"
xmlns:mf="http://example.com/mf"
expand-text="yes">
<xsl:key name="ref" match="mdata/effectivity/effect">
<xsl:variable name="referenced-node" as="node()?">
<xsl:evaluate xpath="@path" context-item="$main-doc" as="node()?"/>
</xsl:variable>
<xsl:sequence select="generate-id($referenced-node)"/>
</xsl:key>
<xsl:variable name="main-doc" select="/"/>
<xsl:mode on-no-match="shallow-copy"/>
<xsl:template match="info/action">
<xsl:copy>
<xsl:apply-templates select="@*, key('ref', generate-id(), $meta)/@effrg"/>
</xsl:copy>
</xsl:template>
<xsl:param name="meta">
<mdata>
<metadata>
<metadata-item name="n1" value="v1" />
<metadata-item name="n2" value="v2" />
<metadata-item name="n3" value="v3" />
</metadata>
<effectivity>
<effect path="//*[@lid='a']" effrg="0074 0080 0087" />
<effect path="//*[@lid='b']" effrg="0136 0146 0174" />
</effectivity>
</mdata>
</xsl:param>
</xsl:stylesheet>
如果您想匹配其他文档中引用的任何元素并复制除path
之外的任何属性,您可以将具有match="info/action"
的模板更改为
<xsl:template match="*[key('ref', generate-id(), $meta)]">
<xsl:copy>
<xsl:apply-templates select="@*, key('ref', generate-id(), $meta)/(@* except @path), node()"/>
</xsl:copy>
</xsl:template>
【讨论】:
感谢您的回答。我看到您在模板上添加了 match="info/action"。问题是,我不能确定 xpath 是否会在 /info/action 上进行评估。它必须是动态的。从技术上讲,它可以评估为 /foo/bar 然后必须将 effrg 添加到 bar 标签。如何解决这个问题? @sboti8m,我认为您的第一个输入文档是 XSLT 的主要输入文档,并使用匹配模板处理其中的元素,尝试根据您显示的示例使用一些精确的匹配模式。这就是为什么代码显然是xsl:template match="info/action"
。这与 XPath 评估无关,它发生在我设置的函数的其他地方。根据样本,我确实假设path
属性选择action
元素或什么都不选择,正如as="element(action)?"
在函数上声明的那样。没有它,该功能将起作用。
如果输入没有任何 foo/bar
元素,那么说“从技术上讲,它可以评估为 /foo/bar,然后必须将 effrg 添加到 bar 标签”是没有意义的. xsl:evaluate
将查找元素(如果存在)以及要处理的代码,例如effrg
属性不会改变。您还可以将match="info/action"
更改为更通用的名称,但如果没有明确的描述,您无法猜测和编码您的 XML 可能具有的所有选项和变体。因此,您需要编辑问题并更具体地说明您希望更改的主要 XML 中的哪些元素
您好,谢谢您的回答。可以假设,xpath 将评估为有效节点。因此,为了使其更通用,我从 info/action 更改为 node()。但这似乎不起作用。您的解决方案运行良好,但 module_meta.xml 文件可能会更改,而 module.xml 保持不变。因此可能会出现不同的 xpath,它评估为完全不同的节点(比如说 procbody/info)匹配中的“硬编码”信息/动作不够通用。
@sboti8m,看看我添加的最后一个建议是否有帮助,匹配模式没有硬编码以使该方法有效,而是从您给定的输入结构中推断出来的。但是 XSLT 肯定足够灵活,可以匹配所有元素 (match="*"
) 或所有引用的元素(请参阅答案的编辑)。以上是关于XSL 从另一个文档评估动态 XPATH的主要内容,如果未能解决你的问题,请参考以下文章
用XPath精确定位节点元素&selenium使用Xpath定位之完整篇