XPath返回所有ANCESTORS直到

JPM*_*JPM 5 xslt xpath

这是XPath返回所有兄弟姐妹的常见请求的变体,直到某些条件,由Dimitre Novatchev在XPath轴上回答特征丰满,得到所有后续节点,直到使用此模式:

$x/following-sibling::p
   [1 = count(preceding-sibling::node()[name() = name($x)][1] | $x)]
Run Code Online (Sandbox Code Playgroud)

但是,这种模式依赖的对称性following-siblingpreceding-sibling,在沿轴的两个方向看的能力.

轴是否有类似的模式ancestor-or-self

例如:

<t>
  <a xml:base="/news/" >
    <b xml:base="reports/">
      <c xml:base="politics/" />
      <c xml:base="sports/" >
        <d xml:base="reports/" />
        <d xml:base="photos/" >
          <img url="A1.jpg" />
          <img url="A2.jpg" />
        </d>
      </c>
      <c xml:base="entertainment" />
    </b>
  </a>
</t>
Run Code Online (Sandbox Code Playgroud)

直截了当

<xsl:template match="img">

    <xsl:for-each select="ancestor-or-self::*[@xml:base]">
        <xsl:value-of select="@xml:base"/>
    </xsl:for-each>

    <xsl:value-of select="@url"/>

</xsl:template>
Run Code Online (Sandbox Code Playgroud)

会回来的

 /news/reports/sports/photos/A1.jpg
 /news/reports/sports/photos/A1.jpg
Run Code Online (Sandbox Code Playgroud)

但如果

      <c xml:base="sports/" >
Run Code Online (Sandbox Code Playgroud)

相反

      <c xml:base="/sports/" >
Run Code Online (Sandbox Code Playgroud)

随着领先/,for-each需要停止,以便返回

 /sports/photos/A1.jpg
 /sports/photos/A2.jpg
Run Code Online (Sandbox Code Playgroud)

如何(在XSLT/XPath 1.0中)使其停止?

Dim*_*hev 3

此 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:param name="pWanted" select="//img"/>
 <xsl:param name="pWantedAttr" select="'url'"/>

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

 <xsl:template match="*[not(starts-with(@xml:base, '/'))]">
  <xsl:apply-templates select="ancestor::*[@xml:base][1]"/>
  <xsl:value-of select="concat(@xml:base,@*[name()=$pWantedAttr])"/>
  <xsl:if test="not(@xml:base)"><xsl:text>&#xA;</xsl:text></xsl:if>
 </xsl:template>

 <xsl:template match="*[starts-with(@xml:base, '/')]">
  <xsl:value-of select="@xml:base"/>
 </xsl:template>
</xsl:stylesheet>
Run Code Online (Sandbox Code Playgroud)

当应用于此 XML 文档时

<t>
  <a xml:base="/news/" >
    <b xml:base="reports/">
      <c xml:base="politics/" />
      <c xml:base="/sports/" >
        <d xml:base="reports/" />
        <d xml:base="photos/" >
          <img url="A1.jpg" />
          <img url="A2.jpg" />
        </d>
      </c>
      <c xml:base="entertainment" />
    </b>
  </a>
</t>
Run Code Online (Sandbox Code Playgroud)

产生想要的正确结果:

/sports/photos/A1.jpg
/sports/photos/A2.jpg
Run Code Online (Sandbox Code Playgroud)

更新 - 单个 XPath 2.0 表达式解决方案

   for $target in //img,
       $top in $target/ancestor::*[starts-with(@xml:base,'/')][1]
    return
      string-join(
         (
             $top/@xml:base
           , $top/descendant::*
                [@xml:base and . intersect $target/ancestor::*]
                   /@xml:base
           , $target/@url,
           '&#xA;'
        ),
        ''
                )
Run Code Online (Sandbox Code Playgroud)

基于 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:template match="/">
     <xsl:sequence select=
      "for $target in //img,
           $top in $target/ancestor::*[starts-with(@xml:base,'/')][1]
        return
          string-join(
             (
                 $top/@xml:base
               , $top/descendant::*
                    [@xml:base and . intersect $target/ancestor::*]
                       /@xml:base
               , $target/@url,
               '&#xA;'
            ),
            ''
                    )
      "/>
 </xsl:template>
</xsl:stylesheet>
Run Code Online (Sandbox Code Playgroud)

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

<t>
  <a xml:base="/news/" >
    <b xml:base="reports/">
      <c xml:base="politics/" />
      <c xml:base="sports/" >
        <d xml:base="reports/" />
        <d xml:base="photos/" >
          <img url="A1.jpg" />
          <img url="A2.jpg" />
        </d>
      </c>
      <c xml:base="entertainment" />
    </b>
  </a>
</t>
Run Code Online (Sandbox Code Playgroud)

对 XPath 表达式进行求值,并将求值结果复制到输出:

/news/reports/sports/photos/A1.jpg
 /news/reports/sports/photos/A2.jpg
Run Code Online (Sandbox Code Playgroud)

修改后的文档

<t>
  <a xml:base="/news/" >
    <b xml:base="reports/">
      <c xml:base="politics/" />
      <c xml:base="/sports/" >
        <d xml:base="reports/" />
        <d xml:base="photos/" >
          <img url="A1.jpg" />
          <img url="A2.jpg" />
        </d>
      </c>
      <c xml:base="entertainment" />
    </b>
  </a>
</t>
Run Code Online (Sandbox Code Playgroud)

再次产生想要的正确结果:

/sports/photos/A1.jpg
 /sports/photos/A2.jpg
Run Code Online (Sandbox Code Playgroud)

更新2

OP 建议进行这种简化:

原海报添加的更新:一旦嵌入到完整的应用程序中,其中完整的 url 取代了相关的 url,Dimitre 的方法最终变得如此简单

:

<xsl:template match="@url">
    <xsl:attribute name="url">
        <xsl:apply-templates mode="uri" select=".." />
        <xsl:value-of select="."/>
    </xsl:attribute>
</xsl:template>

<xsl:template match="*"  mode="uri">
    <xsl:if test="not(starts-with(@xml:base, '/'))">
        <xsl:apply-templates select="ancestor::*[@xml:base][1]" mode="uri"/>
    </xsl:if>
    <xsl:value-of select="@xml:base"/>
</xsl:template>
Run Code Online (Sandbox Code Playgroud)