Xpath 用于查找存在于两个不同自闭合标签中的元素

问题描述 投票:0回答:2

我正确的 XML 是:

<xml>
   <astart/>
     <a>XXX</a>
      <bstart/>
       <x>..</x>
       <y>..</y>
        <b>AAA</b>
       <x>..</x>
       <y>..</y>
      <bend/>
    <aend/>
    <astart/>
      <bstart/>
       <x>..</x>
       <y>..</y>
        <b>BBB</b>
       <x>..</x>
       <y>..</y>
      <bend/>
    <aend/>
</xml>

不正确的 XML 示例,但场景可能不正确:

    <xml>
      <astart/>
     <a>XXX</a>
       <x>..</x>
       <y>..</y>
      
        <b>AAA</b>
      
    <aend/>
    <astart/>
      <bend/>
        <b>BBB</b>
       <x>..</x>
       <y>..</y> 
    <aend/>
   </xml>

我想在“

<b>
”中找到“
<astart/>...<bstart/>..<b>..</b>... <bend/>...<aend/>
”标签。如果不匹配此模式需要 Xpath 来识别。

我尝试了代码

//b[preceding-sibling::astart[1][not(preceding-sibling::bstart[1])]][following-sibling::aend[1][not(following-sibling::bend[1])]]
,但它不适用于不正确的模式。注意:所有标签都是兄弟姐妹,而不是父母或祖先,标签是可选的。

xml xpath xslt-1.0 xslt-2.0 xpath-2.0
2个回答
0
投票

使用 XPath 3.1 听起来好像以下使用

<<
>>
运算符以及
for-each-pair
可以帮助:

let $root := /,
    $astarts := //astart,
    $aends := //aend
return
    for-each-pair(
      $astarts, 
      $aends, 
      function($s, $e) {
        let $bstarts := $root//bstart[. >> $s and . << $e],
            $bends := $root//bend[. >> $s and . << $e]
        return
            for-each-pair(
              $bstarts, 
              $bends, 
              function($s, $e) {
                $root//b[. >> $s and . << $e]
              }
            )
      }
    )

XQuery 的窗口功能似乎也很适合:

for tumbling window $w in /xml/*
start $s when $s instance of element(astart)
end $e when $e instance of element(aend)
return
  for tumbling window $w in $w
  start $s when $s instance of element(bstart)
  end $e when $e instance of element(bend)
  return $w[self::b]

或者在 XSLT 2/3 中你可以嵌套

for-each-group group-starting-with/group-ending-with
,以下示例尝试仅使用 XSLT 2(但我还没有完全检查):

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    xmlns:mf="http://example.com/mf"
    exclude-result-prefixes="#all"
    version="2.0">
  
  <xsl:param name="start-suffix" as="xs:string" select="'start'"/>
  <xsl:param name="end-suffix" as="xs:string" select="'end'"/>
  
  <xsl:function name="mf:wrapped-elements" as="element()*">
    <xsl:param name="container" as="node()"/>
    <xsl:param name="outer-wrapper" as="xs:string"/>
    <xsl:param name="inner-wrapper" as="xs:string"/>
    <xsl:param name="wrapped" as="xs:string"/>
    <xsl:for-each-group select="$container/*" group-starting-with="*[local-name() = concat($outer-wrapper, $start-suffix)]">
      <xsl:if test="self::*[local-name() = concat($outer-wrapper, $start-suffix)]">
        <xsl:for-each-group select="subsequence(current-group(), 2)" group-ending-with="*[local-name() = concat($outer-wrapper, $end-suffix)]">
          <xsl:if test="current-group()[last()][self::*[local-name() = concat($outer-wrapper, $end-suffix)]]">
            <xsl:for-each-group select="subsequence(current-group(), 1, count(current-group()) - 1)" group-starting-with="*[local-name() = concat($inner-wrapper, $start-suffix)]">
              <xsl:if test="self::*[local-name() = concat($inner-wrapper, $start-suffix)]">
                <xsl:for-each-group select="subsequence(current-group(), 2)" group-ending-with="*[local-name() = concat($inner-wrapper, $end-suffix)]">
                  <xsl:if test="current-group()[last()][self::*[local-name() = concat($inner-wrapper, $end-suffix)]]">
                    <xsl:sequence select="current-group()[self::*[local-name() = $wrapped]]"/>
                  </xsl:if>
                </xsl:for-each-group>
              </xsl:if>
            </xsl:for-each-group>
          </xsl:if>
        </xsl:for-each-group>
      </xsl:if>
    </xsl:for-each-group>
  </xsl:function>

  <xsl:template match="/*">
    <xsl:copy>
      <xsl:sequence select="mf:wrapped-elements(., 'a', 'b', 'b')"/>
    </xsl:copy>
  </xsl:template>
  
  <xsl:output indent="yes"/>
  
</xsl:stylesheet>

0
投票

在我看来,您想检测错误嵌套的

astart
/
aend
bstart
/
bend
对,但我不确定,因为您只谈论
b
元素。

无论如何,下面的 XSLT 1.0 样式表找到第一个错误嵌套对并将其报告为

<bend number="1" expected="a"/>
,这意味着第一个
bend
元素是错误的,因为
aend
是预期的。

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
  <xsl:output method="xml" indent="yes"/>
  <xsl:template match="xml">
    <xsl:apply-templates select="*[1]">
      <xsl:with-param name="stack"/>
    </xsl:apply-templates>
  </xsl:template>
  <xsl:template match="astart | bstart">
    <xsl:param name="stack"/>
    <xsl:apply-templates select="following-sibling::*[1]">
      <xsl:with-param name="stack" select="concat(substring(name(current()),1,1),' ',$stack)"/>
    </xsl:apply-templates>
  </xsl:template>
  <xsl:template match="aend | bend">
    <xsl:param name="stack"/>
    <xsl:choose>
      <xsl:when test="substring-before($stack,' ') = substring(name(current()),1,1)">
        <xsl:apply-templates select="following-sibling::*[1]">
          <xsl:with-param name="stack" select="substring-after($stack,' ')"/>
        </xsl:apply-templates>
      </xsl:when>
      <xsl:otherwise>
        <xsl:copy>
          <xsl:attribute name="number">
            <xsl:value-of select="count(preceding-sibling::*[name()=name(current())]) + 1"/>
          </xsl:attribute>
          <xsl:attribute name="expected">
            <xsl:value-of select="substring-before($stack,' ')"/>
            <xsl:text>end</xsl:text>
          </xsl:attribute>
        </xsl:copy>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:template>
  <xsl:template match="*">
    <xsl:param name="stack"/>
    <xsl:apply-templates select="following-sibling::*[1]">
      <xsl:with-param name="stack" select="$stack"/>
    </xsl:apply-templates>
  </xsl:template>
</xsl:stylesheet>

对于您的第一个(正确的)示例,此样式表不输出任何内容。

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