当满足两个属性条件时加入彼此相邻的兄弟姐妹

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>

并且我尝试在满足以下两个条件时合并段落兄弟节点:

  1. 如果兄弟姐妹具有相同的 y 属性值并且,
  2. 如果以下兄弟 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>.