Xpath查找出现在两个不同的自关闭标记中的元素



我正确的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])]],但它不能为不正确的模式工作。注意:所有标签都是兄弟标签,而不是父标签或祖先标签,并且标签是可选的。

使用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>

在我看来,你想检测不正确嵌套的astart/aendbstart/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>

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