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