如何在XSLT的输出中包含通过xpath参数选择的节点的祖先分支

Bru*_*aim 8 xslt xpath xslt-2.0

尝试了8个多小时后,我希望有人可以帮助我:

给出一本书的以下(简化)XML:

<book>
    <section name="A">
        <chapter name="I">
            <paragraph name="1"/>
            <paragraph name="2"/>
        </chapter>
        <chapter name="II">
            <paragraph name="1"/>          
        </chapter>
    </section>
    <section name="B">
        <chapter name="III"/>
        <chapter name="IV"/>   
    </section>
</book>
Run Code Online (Sandbox Code Playgroud)

我能够使用以下XSL基于给定参数提取书籍XML的任何部分(部分,章节或段落):

<xsl:param name="subSelectionXPath" required="yes" as="node()"/>

<xsl:template match="/">
    <xsl:apply-templates select="$subSelectionXPath"/>
</xsl:template>

<xsl:template match="*">
    <!-- output node with all children -->
    <xsl:copy-of select="."/>
</xsl:template>
Run Code Online (Sandbox Code Playgroud)

和参数$ subSelectionXPath的值类似

doc(filename)//chapter[@name='II']

产生输出:

<chapter name="II">
    <paragraph name="1"/>          
</chapter>
Run Code Online (Sandbox Code Playgroud)

我想要实现的另一个目标是让祖先XML分支包含所选的XML片段,即:

<book>
    <section name="A">
        <chapter name="II">
            <paragraph name="1"/>          
        </chapter>
    </section>    
</book>
Run Code Online (Sandbox Code Playgroud)

我想(并尝试)遍历XML树并测试当前节点是否是祖先,类似于(伪代码):

<xsl:if test="node() in $subSelectionXPath/ancestor::node()">
    <xsl:copy>
       <xsl:apply-templates/>
    </xsl:copy>
</xsl:if>
Run Code Online (Sandbox Code Playgroud)

我也尝试过xsl:key但是我担心我的XSLT知识在这里结束了.有什么想法吗?

Dim*_*hev 6

从您的代码中可以明显看出您使用的是XSLT 2.0.

这个XSLT 2.0转换:

<xsl:stylesheet version="2.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>
 <xsl:strip-space elements="*"/>

 <xsl:param name="subSelectionXPath"
  as="node()" select="//chapter[@name='II']"
  />

 <xsl:template match="*[descendant::node() intersect $subSelectionXPath]">
  <xsl:copy>
   <xsl:copy-of select="@*"/>
   <xsl:apply-templates select="*"/>
  </xsl:copy>
 </xsl:template>

 <xsl:template match="*[. intersect $subSelectionXPath]">
  <xsl:copy-of select="."/>
 </xsl:template>
</xsl:stylesheet>
Run Code Online (Sandbox Code Playgroud)

当应用于提供的XML文档时:

<book>
    <section name="A">
        <chapter name="I">
            <paragraph name="1"/>
            <paragraph name="2"/>
        </chapter>
        <chapter name="II">
            <paragraph name="1"/>
        </chapter>
    </section>
    <section name="B">
        <chapter name="III"/>
        <chapter name="IV"/>
    </section>
</book>
Run Code Online (Sandbox Code Playgroud)

产生完全想要的,正确的结果:

<book>
   <section name="A">
      <chapter name="II">
         <paragraph name="1"/>
      </chapter>
   </section>
</book>
Run Code Online (Sandbox Code Playgroud)

说明:我们只有两个模板:

  1. 与后代与$subSelectionXPath节点集具有非空交集的任何元素匹配的模板.在这里,我们"浅层复制"元素并将模板应用于其子元素.

  2. 匹配属于$subSelectionXPath节点集的元素的模板.在这里,我们复制以该元素为根的整个子树.

  3. 请注意XPath 2.0 intersect运算符的使用.

  4. 没有明确的递归.

II.XSLT 1.0解决方案:

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>
 <xsl:strip-space elements="*"/>

 <xsl:param name="subSelectionXPath"
  select="//chapter[@name='II']"
  />

 <xsl:template match="*">
  <xsl:choose>
   <xsl:when test=
   "descendant::node()
        [count(.|$subSelectionXPath)
        =
         count($subSelectionXPath)
        ]
   ">
   <xsl:copy>
    <xsl:copy-of select="@*"/>
    <xsl:apply-templates select="*"/>
   </xsl:copy>
  </xsl:when>

   <xsl:when test=
   "count(.|$subSelectionXPath)
   =
    count($subSelectionXPath)
   ">
   <xsl:copy-of select="."/>
   </xsl:when>
  </xsl:choose>
 </xsl:template>
</xsl:stylesheet>
Run Code Online (Sandbox Code Playgroud)

当此转换应用于同一XML文档(如上所示)时,将生成相同的想要和正确的结果:

<book>
   <section name="A">
      <chapter name="II">
         <paragraph name="1"/>
      </chapter>
   </section>
</book>
Run Code Online (Sandbox Code Playgroud)

解释:这基本上是XSLT 2.0解决方案,其中的XPath 2.0 intersect操作者是使用公知的Kayessian(对于@迈克尔凯)式两个节点集的交集翻译成XPath 1.0中$ns1$ns2:

$ns1[count(.|$ns2) = count($ns2)]
Run Code Online (Sandbox Code Playgroud)