我正确的 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
/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>
对于您的第一个(正确的)示例,此样式表不输出任何内容。