我有以下一组文件。
SourceFile.xml.ParamerterSettings.xml
<?xml version="1.0" encoding="utf-8" ?>
<Employees>
<Employee id="1">
<firstname relationship="headnote">Atif</firstname>
<lastname relationship="lname">Bashir</lastname>
<age relationship="age">32</age>
</Employee>
</Employees>
ParamerterSettings.xml
<?xml version="1.0" encoding="utf-8"?>
<Settings>
<Employee id="1">
<sourceFile>Lookup1.xml</sourceFile>
<sourceXpathfield>Employees/Employee[@id</sourceXpathfield>
<lookupXpathfield>Employees/Employee[@id='1']</lookupXpathfield>
<elementstoinsert>xyz</elementstoinsert>
</Employee>
</Settings>
Lookup.xml
<?xml version="1.0" encoding="utf-8"?>
<Employees>
<Employee id="1">
<department code="102">HR</department>
</Employee>
</Employees>
变换.xsl
<?xml version="1.0" encoding="UTF-8" ?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema" exclude-result-prefixes="xs" version="2.0">
<xsl:include href="identity.xsl"/>
<xsl:param name="EmployeeId" select="'1,2'" />
<xsl:variable name="FileSettings" select="document('test3.xml')" />
<xsl:variable name="SuppressSetting" select="$FileSettings/Settings/Employee[@id = tokenize($EmployeeId, ',')]" />
<xsl:template match="Employee">
<xsl:copy>
<xsl:apply-templates select="@*"/>
<xsl:apply-templates select="publisher" />
<xsl:apply-templates select="node() except publisher"/>
<xsl:variable name="outerfile" select="document($SuppressSetting/sourceFile)"></xsl:variable>
<xsl:variable name="outerfiledetails" select="$outerfile/$SuppressSetting/lookupXpathfield"></xsl:variable>
<xsl:value-of select="$outerfiledetails"></xsl:value-of>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
输出应该是。
<?xml version="1.0" encoding="utf-8" ?>
<Employees>
<Employee id="1">
<firstname relationship="headnote">Atif</firstname>
<lastname relationship="lname">Bashir</lastname>
<age relationship="age">32</age>
HR
</Employee>
</Employees>
我修改了Transform.xsl中的下面一行。
<xsl:variable name="outerfiledetails" select="$outerfile/$SuppressSetting/lookupXpathfield"></xsl:variable>
变成
<xsl:variable name="outerfiledetails" select="$outerfile/Employees/Employee[@id='1']"></xsl:variable>
然后,我得到了我的输出,但我想保留两个XPath表达式。SourceFile.xml
和 Lookup.xml
变成 ParamerterSettings.xml
这样我就可以写一个更通用的脚本。除了动态xpath之外,还可以用其他方式来实现吗?任何关于如何实现同样的想法或提示都将被高度赞赏。
在纯XSLT 1.0或2.0中,动态XPath评估是不可能的。
在 "混合 "解决方案中,至少有三种方法可以做到这一点。:
I. 使用EXSLT函数 dyn:evaluate()
不幸的是,很少有XSLT 1.0处理器实现了 dyn:evaluate()
.
二、用XSLT处理XML文档,并生成包含XPath表达式的新XSLT文件--然后执行新生成的转换。用XSLT处理XML文档,并生成一个包含XPath表达式的新XSLT文件--然后执行新生成的转换。.
很少有人这样做,在我看来,这比下一个解决方案更复杂。
三.办法 XPath展示器 作品
我们的想法是:在XSLT样式表中定义一个全局变量。
在XSLT样式表中定义一个全局变量,就像这样:
<xsl:variable name="vExpression" select="dummy"/>
然后,使用DOM将样式表加载为XML文档,并替换为 select
属性 的 vExpression
变量,并使用源 XML 文档中包含的实际 XPath 表达式。
最后,启动转换 使用加载到内存并动态更新的xslt样式表。
在XSLT 2.0中你不能这样做,但在最新版本的XSLT中你可以这样做。
是的,我们可以......至少是初步的。 这里有一个我在Saxon CE (XSLT 2.0)中使用的变通方法,直到 "evaluation "功能可用。也许这并不适用于所有类型的复杂XML文档,但你可以根据你的需要调整 "过滤器"(查询属性等)。
在我的特殊情况下,我的xPath表达式描述了通往元素的 "完整 "路径,包括它们的名称,技巧是使用通配符与动态xPath表达式的最后一个元素相结合,例如,使用 "third "而不是 "firstsecondthird"。
<xsl:variable name="value" select="//*[name() = 'third']" />
为了限制结果(将选择所有名称为 "third "的元素),你必须对祖先 "first "和 "second "也进行查询。也许有人有办法简化下面的代码,特别是祖先的调用。
<!-- global variable which holds a XML document with root node "data" -->
<xsl:variable name="record" select="document('record.xml')/data"/>
<!-- select elements from the global "record" variable using dynamic xpath expressions -->
<xsl:function name="utils:evaluateXPath">
<xsl:param name="xpath" as="xs:string"/>
<xsl:choose>
<xsl:when test="function-available('evaluate')">
<!-- modify the code if the function has been implemented :-) -->
<xsl:value-of select="'function evaluate() can be used ...'"/>
</xsl:when>
<xsl:otherwise>
<!-- get a list of elements defined in the xpath expression -->
<xsl:variable name="sequence" select="tokenize($xpath, '/')" />
<!-- get the number of ancestors for the last element -->
<xsl:variable name="iAncestors" select="count($sequence)-1" as="xs:integer" />
<!-- get the last element from the xpath expression -->
<xsl:variable name="lastElement" select="if ($iAncestors > 0) then $sequence[last()] else $xpath" />
<!-- try to find the desired element as defined in xpath expression -->
<!-- use parenthesis to grab only the first occurrence -->
<xsl:value-of select="
if ($iAncestors = 0) then
($record//*[name() = $lastElement and not(*)])[1]
else if ($iAncestors = 1) then
($record//*[name() = $lastElement and not(*) and (name(..) = $sequence[1])])[1]
else if ($iAncestors = 2) then
($record//*[name() = $lastElement and not(*) and (name(..) = $sequence[2]) and (name(../..) = $sequence[1])])[1]
else if ($iAncestors = 3) then
($record//*[name() = $lastElement and not(*) and (name(..) = $sequence[3]) and (name(../..) = $sequence[2]) and (name(../../..) = $sequence[1])])[1]
else if ($iAncestors = 4) then
($record//*[name() = $lastElement and not(*) and (name(..) = $sequence[4]) and (name(../..) = $sequence[3]) and (name(../../..) = $sequence[2]) and (name(../../../..) = $sequence[1])])[1]
else if ($iAncestors = 5) then
($record//*[name() = $lastElement and not(*) and (name(..) = $sequence[5]) and (name(../..) = $sequence[4]) and (name(../../..) = $sequence[3]) and (name(../../../..) = $sequence[2]) and (name(../../../../..) = $sequence[1])])[1]
else 'failure: too much elements for evaluating dyn. xpath ... add another level!'"
/>
</xsl:otherwise>
</xsl:choose>
</xsl:function>
对于我的目的来说,只有第一个匹配的元素才会被返回,而且没有子节点。也许你必须根据你的特殊需求来调整。
一个可能的技巧是调用一个参数化的 xsl:template
它执行关键的选择比较部分,并将其输出评估为字符串。请看这个答案