当满足两个属性条件时加入彼此相邻的兄弟姐妹
Join siblings next to each other when two attribute conditions are met
我有以下 XML:
<?xml version="1.0" encoding="UTF-8"?>
<ROOT>
<p width="17.76" x="81.6" y="270.708">
<span x="81.6" y="270.708" base="273.9" width="17.76" height="4.368">Copy</span>
</p>
<p width="22.32" x="101.52" y="270.708">
<span x="101.52" y="270.708" base="273.9" width="22.32" height="4.368">mailed</span>
</p>
<p width="6.24" x="126" y="270.708">
<span x="126" y="270.708" base="273.9" width="6.24" height="4.368">to</span>
</p>
<p width="15.12" x="134.4" y="270.708">
<span x="134.4" y="270.708" base="273.9" width="15.12" height="4.368">third</span>
</p>
<p width="23.04" x="151.68" y="270.708">
<span x="151.68" y="270.708" base="273.9" width="23.04" height="4.368">parties</span>
</p>
<p width="2.64" x="176.88" y="270.708">
<span x="176.88" y="270.708" base="273.9" width="2.64" height="4.368">-</span>
</p>
<p width="12.24" x="181.68" y="270.708">
<span x="181.68" y="270.708" base="273.9" width="12.24" height="4.368">see</span>
</p>
<p width="16.8" x="196.08" y="270.708">
<span x="196.08" y="270.708" base="273.9" width="16.8" height="4.368">page</span>
</p>
<p width="8.64" x="215.04" y="270.708">
<span x="215.04" y="270.708" base="273.9" width="8.64" height="4.368">33</span>
</p>
</ROOT>
并且我尝试在满足以下两个条件时合并段落兄弟节点:
- 如果兄弟姐妹具有相同的 y 属性值并且,
- 如果以下兄弟 x 属性值 - (当前宽度和当前 x 属性值之和)< 4
我有应用上述第一个条件的代码,但我无法正确应用第二个条件。我正在使用递归方法,我猜这涉及复杂的分组。
应用上述条件后,输出应如下所示:
<?xml version="1.0" encoding="UTF-8"?>
<ROOT>
<p width="17.76" x="81.6" y="270.708">
<span x="81.6" y="270.708" base="273.9" width="17.76" height="4.368">Copy</span>
<span x="101.52" y="270.708" base="273.9" width="22.32" height="4.368">mailed</span>
<span x="126" y="270.708" base="273.9" width="6.24" height="4.368">to</span>
<span x="134.4" y="270.708" base="273.9" width="15.12" height="4.368">third</span>
<span x="151.68" y="270.708" base="273.9" width="23.04" height="4.368">parties</span>
<span x="176.88" y="270.708" base="273.9" width="2.64" height="4.368">-</span>
<span x="181.68" y="270.708" base="273.9" width="12.24" height="4.368">see</span>
<span x="196.08" y="270.708" base="273.9" width="16.8" height="4.368">page</span>
<span x="215.04" y="270.708" base="273.9" width="8.64" height="4.368">33</span>
</p>
</ROOT>
这是我现在拥有的代码是:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="2.0">
<xsl:template match="node() | @*">
<xsl:copy>
<xsl:apply-templates select="@* | node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="ROOT">
<xsl:copy>
<xsl:copy-of select="@*" />
<xsl:apply-templates select="p[not(preceding-sibling::p/@y = @y)]" mode="sibling-join" />
</xsl:copy>
</xsl:template>
<xsl:template match="p" mode="sibling-join">
<xsl:copy>
<xsl:apply-templates select="node() | @*" />
<xsl:apply-templates select="following-sibling::p[current()/@y = @y]" />
</xsl:copy>
</xsl:template>
<xsl:template match="p">
<xsl:apply-templates select="node()" />
</xsl:template>
</xsl:stylesheet>
由于某些 XSLT 2 处理器(如 Saxon 9 或 10、Altova Raptor 或 XmlPrime)也支持 XQuery 3,因此使用 XQuery 3 window
子句来制定此类条件可能更方便,因为它明确允许您形成 end
条件,您可以在其中访问变量绑定,将“window/group”中的“最后”项与以下项 (next
) 进行比较:
<ROOT>
{
for tumbling window $w in ROOT/p
start when true()
end $e next $n when $e/@y != $n/@y or $n/@x - $e!(@width + @x) ge 4
return
<p>
{
head($w)/@*, $w/node()
}
</p>
}
</ROOT>
https://xqueryfiddle.liberty-development.net/3Nzd8bU
在 XSLT 2/3 中使用 for-each-group group-starting-with
并假设分组总体是您可以使用的兄弟序列
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="#all"
version="3.0">
<xsl:mode on-no-match="shallow-copy"/>
<xsl:template match="ROOT">
<xsl:copy>
<xsl:for-each-group select="p"
group-starting-with="p[1] | p[preceding-sibling::p[1][@y != current()/@y or (@x - current()!(@width + @x) ge 4)]]">
<xsl:copy>
<xsl:apply-templates select="@*, current-group()/node()"/>
</xsl:copy>
</xsl:for-each-group>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
https://xsltfiddle.liberty-development.net/naZYrpE/7
所使用的表达式 current()!(@with + @x)
是 XSLT/XPath 3 但您可以在 XSLT 2 中使用 current()/(@with + @x)
。而不是使用声明式 xsl:mode
来设置身份转换需要拼出模板 <xsl:template match="@* | node()"><xsl:copy><xsl:apply-templates select="@* | node()"/></xsl:copy></xsl:template>
.
我有以下 XML:
<?xml version="1.0" encoding="UTF-8"?>
<ROOT>
<p width="17.76" x="81.6" y="270.708">
<span x="81.6" y="270.708" base="273.9" width="17.76" height="4.368">Copy</span>
</p>
<p width="22.32" x="101.52" y="270.708">
<span x="101.52" y="270.708" base="273.9" width="22.32" height="4.368">mailed</span>
</p>
<p width="6.24" x="126" y="270.708">
<span x="126" y="270.708" base="273.9" width="6.24" height="4.368">to</span>
</p>
<p width="15.12" x="134.4" y="270.708">
<span x="134.4" y="270.708" base="273.9" width="15.12" height="4.368">third</span>
</p>
<p width="23.04" x="151.68" y="270.708">
<span x="151.68" y="270.708" base="273.9" width="23.04" height="4.368">parties</span>
</p>
<p width="2.64" x="176.88" y="270.708">
<span x="176.88" y="270.708" base="273.9" width="2.64" height="4.368">-</span>
</p>
<p width="12.24" x="181.68" y="270.708">
<span x="181.68" y="270.708" base="273.9" width="12.24" height="4.368">see</span>
</p>
<p width="16.8" x="196.08" y="270.708">
<span x="196.08" y="270.708" base="273.9" width="16.8" height="4.368">page</span>
</p>
<p width="8.64" x="215.04" y="270.708">
<span x="215.04" y="270.708" base="273.9" width="8.64" height="4.368">33</span>
</p>
</ROOT>
并且我尝试在满足以下两个条件时合并段落兄弟节点:
- 如果兄弟姐妹具有相同的 y 属性值并且,
- 如果以下兄弟 x 属性值 - (当前宽度和当前 x 属性值之和)< 4
我有应用上述第一个条件的代码,但我无法正确应用第二个条件。我正在使用递归方法,我猜这涉及复杂的分组。
应用上述条件后,输出应如下所示:
<?xml version="1.0" encoding="UTF-8"?>
<ROOT>
<p width="17.76" x="81.6" y="270.708">
<span x="81.6" y="270.708" base="273.9" width="17.76" height="4.368">Copy</span>
<span x="101.52" y="270.708" base="273.9" width="22.32" height="4.368">mailed</span>
<span x="126" y="270.708" base="273.9" width="6.24" height="4.368">to</span>
<span x="134.4" y="270.708" base="273.9" width="15.12" height="4.368">third</span>
<span x="151.68" y="270.708" base="273.9" width="23.04" height="4.368">parties</span>
<span x="176.88" y="270.708" base="273.9" width="2.64" height="4.368">-</span>
<span x="181.68" y="270.708" base="273.9" width="12.24" height="4.368">see</span>
<span x="196.08" y="270.708" base="273.9" width="16.8" height="4.368">page</span>
<span x="215.04" y="270.708" base="273.9" width="8.64" height="4.368">33</span>
</p>
</ROOT>
这是我现在拥有的代码是:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="2.0">
<xsl:template match="node() | @*">
<xsl:copy>
<xsl:apply-templates select="@* | node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="ROOT">
<xsl:copy>
<xsl:copy-of select="@*" />
<xsl:apply-templates select="p[not(preceding-sibling::p/@y = @y)]" mode="sibling-join" />
</xsl:copy>
</xsl:template>
<xsl:template match="p" mode="sibling-join">
<xsl:copy>
<xsl:apply-templates select="node() | @*" />
<xsl:apply-templates select="following-sibling::p[current()/@y = @y]" />
</xsl:copy>
</xsl:template>
<xsl:template match="p">
<xsl:apply-templates select="node()" />
</xsl:template>
</xsl:stylesheet>
由于某些 XSLT 2 处理器(如 Saxon 9 或 10、Altova Raptor 或 XmlPrime)也支持 XQuery 3,因此使用 XQuery 3 window
子句来制定此类条件可能更方便,因为它明确允许您形成 end
条件,您可以在其中访问变量绑定,将“window/group”中的“最后”项与以下项 (next
) 进行比较:
<ROOT>
{
for tumbling window $w in ROOT/p
start when true()
end $e next $n when $e/@y != $n/@y or $n/@x - $e!(@width + @x) ge 4
return
<p>
{
head($w)/@*, $w/node()
}
</p>
}
</ROOT>
https://xqueryfiddle.liberty-development.net/3Nzd8bU
在 XSLT 2/3 中使用 for-each-group group-starting-with
并假设分组总体是您可以使用的兄弟序列
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="#all"
version="3.0">
<xsl:mode on-no-match="shallow-copy"/>
<xsl:template match="ROOT">
<xsl:copy>
<xsl:for-each-group select="p"
group-starting-with="p[1] | p[preceding-sibling::p[1][@y != current()/@y or (@x - current()!(@width + @x) ge 4)]]">
<xsl:copy>
<xsl:apply-templates select="@*, current-group()/node()"/>
</xsl:copy>
</xsl:for-each-group>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
https://xsltfiddle.liberty-development.net/naZYrpE/7
所使用的表达式 current()!(@with + @x)
是 XSLT/XPath 3 但您可以在 XSLT 2 中使用 current()/(@with + @x)
。而不是使用声明式 xsl:mode
来设置身份转换需要拼出模板 <xsl:template match="@* | node()"><xsl:copy><xsl:apply-templates select="@* | node()"/></xsl:copy></xsl:template>
.