xslt中的动态xpath?

问题描述 投票:4回答:4

我有以下一组文件。

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.xmlLookup.xml 变成 ParamerterSettings.xml 这样我就可以写一个更通用的脚本。除了动态xpath之外,还可以用其他方式来实现吗?任何关于如何实现同样的想法或提示都将被高度赞赏。

xslt xslt-2.0
4个回答
11
投票

在纯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样式表中定义一个全局变量。

  1. 在XSLT样式表中定义一个全局变量,就像这样:

      <xsl:variable name="vExpression" select="dummy"/>
    
  2. 然后,使用DOM将样式表加载为XML文档,并替换为 select 属性vExpression 变量,并使用源 XML 文档中包含的实际 XPath 表达式。

  3. 最后,启动转换 使用加载到内存并动态更新的xslt样式表。


3
投票

在XSLT 2.0中你不能这样做,但在最新版本的XSLT中你可以这样做。

http:/www.w3.orgTRxslt-21#element-evaluate


2
投票

是的,我们可以......至少是初步的。 这里有一个我在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>

对于我的目的来说,只有第一个匹配的元素才会被返回,而且没有子节点。也许你必须根据你的特殊需求来调整。


0
投票

一个可能的技巧是调用一个参数化的 xsl:template 它执行关键的选择比较部分,并将其输出评估为字符串。请看这个答案

© www.soinside.com 2019 - 2024. All rights reserved.