我有一组文件:
SourceFile.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>
transform.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>
然后我得到了我的输出,但是我想将SourceFile.xml
和Lookup.xml
的XPath表达式保留到ParamerterSettings.xml
中,以便我可以编写更通用的脚本。这可以通过动态xpath以任何其他方式完成吗?任何暗示相同的想法或提示都将受到高度赞赏。
答案 0 :(得分:11)
纯XSLT 1.0或2.0中无法进行动态XPath评估。
在“混合”解决方案中至少有三种方法可以做到这一点:
<强>予。使用EXSLT函数dyn:evaluate()
不幸的是,很少有XSLT 1.0处理器实现dyn:evaluate()
。
<强> II。使用XSLT处理XML文档并生成包含XPath表达式的新XSLT文件 - 然后执行新生成的转换。
很少有人这样做,在我看来,这比下一个解决方案更复杂。
<强> III。 the XPath Visualizer的工作方式
这个想法是:
在XSLT样式表中定义一个全局变量,如下所示:
<xsl:variable name="vExpression" select="dummy"/>
然后,使用DOM将样式表作为XML文档加载,并将select
变量的vExpression
属性替换为包含的实际XPath表达式在源XML文档中。
最后,使用加载到内存并动态更新xslt样式表来启动转换。
答案 1 :(得分:3)
您无法在XSLT 2.0中执行此操作,但您可以在最新版本的XSLT中执行此操作:
答案 2 :(得分:2)
是的,我们可以......至少是初步的。这是一种解决方法,我使用Saxon CE(XSLT 2.0),直到“评估”功能可用。也许这不适用于所有类型的复杂XML文档,但可能您可以根据需要调整“过滤器”(查询属性等)。
在我的特殊情况下,我有xPath表达式描述元素的“完整”路径,包括它们的名称,技巧是将通配符与动态xPath表达式的最后一个元素组合使用,例如:使用“第三”而不是“第一/第二/第三”:
<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>
至于我的目的,只返回没有子节点的第一个匹配元素。可能你必须根据你的特定需求进行调整。
答案 3 :(得分:0)
一个可能的技巧是调用参数化的xsl:template
,该参数执行关键的选择比较部分并将其输出作为字符串进行评估。
See this answer