2011-01-07 51 views
2

我已经follwing文件集:xslt中的动态xpath?

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> 

然后我得到我的输出,但我想将SourceFile.xmlLookup.xml的XPath表达式保留为ParamerterSettings.xml,以便我可以编写更通用的脚本。这可以通过任何其他方式完成,然后动态xpath?任何想法或暗示推动相同将高度赞赏。

+1

这是你已经简化您最初的几乎可怕的问题了良好的进展,但这个问题仍然过于复杂和不明确。尝试改变它并进一步简化它 - 我相信你不需要所有的细节。尤其是,如果要处理两个以上的文件,每个人都会放弃尝试理解问题。太复杂了:我永远不会用这种方式设计XSLT应用程序,并相信我,我有XSLT应用程序的复杂性非常具有挑战性,99%的开发人员不相信XSLT可能会这样做。 – 2011-01-07 20:58:20

+0

嗨Dimitre,我想要的是从外部文件执行xpath值。原因是我有多个exeternal文件,我想从中获取数据并将该数据插回到主源文件中。我可以通过对多个模板进行硬编码来实现,但我想避免这种情况,并根据不同的连接或xpath值将多个文件中的一个模板定义为外部文件中的设置。 – atif 2011-01-08 02:26:49

+0

@ Nick-Jones的答案是正确的:这不能在XSLT/XPath 2.0中完成,它可能由下一个版本提供。但是,我非常怀疑动态XPath评估的必要性 - 如果您很好地描述了您的问题,那么可能有一个解决方案不需要这样做。为什么不直接以最简单的形式提出问题:“我如何评估此XML文档中包含的这个表达式?”。虽然纯粹的XSLT解决方案是不可能的,但至少有三种不同的“混合”解决方案,我知道这个问题。 – 2011-01-10 14:18:30

回答

11

动态XPath计算纯XSLT是不可能的1.0 2.0。

至少有三个方面的 “混合” 解决方案做到这一点:

I.使用EXSLT功能dyn:evaluate()

遗憾的是,很少有XSLT 1.0处理器实现dyn:evaluate()

二,使用XSLT处理XML文档并生成包含XPath表达式的新XSLT文件 - 然后执行新生成的转换

很少有人这样做,在我看来,这比下一个解决方案更复杂。

三,the XPath Visualizer工作

的想法的方式是:

  1. 有这样定义XSLT样式表中的全局变量:

    <xsl:variable name="vExpression" select="dummy"/> 
    
  2. 然后,装入样式表作为使用DOM的XML文档,并与AC替换vExpression变量的select属性包含在源XML文档中的tual XPath表达式。

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

2

是的,我们可以...至少rudimentarily。这是一个解决方案,我使用Saxon CE(XSLT 2.0),直到“评估”功能可用。也许这不适用于各种复杂的XML文档,但可能可以根据需要调整“过滤器”(查询属性等)。

在我的特殊情况下,我有一个描述为“满”的路径元素,包括他们的姓名,唯一的办法是结合使用通配符只有动态XPath表达式的最后一个元素,例如XPath表达式使用“第三”,而不是“第一/二/三”:

<xsl:variable name="value" select="//*[name() = 'third']" /> 

为了限制的结果(会选择名称为“第三”的所有元素),你将不得不对祖先“第一”查询和“第二”也是。也许任何人有一个想法,以简化下面的代码,特别是祖先的呼唤:

<!-- 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> 

至于我的目的只是其中有没有返回子节点的第一个匹配的元素。可能你必须根据你的具体需求来调整它。