XSLT:拆分无继续元素/分组继续元素

时间:2010-03-08 06:32:41

标签: xml xslt

在使用XSLT实现这个问题时需要一些帮助,我已经使用SAX解析器实现了这个问题的Java代码,但由于客户要求更改某些东西,这很麻烦。

所以我们现在正在使用XSLT,而不需要编译并部署到Web服务器。我有下面的XML。

示例1:

<ShotRows>
  <ShotRow row="3" col="3" bit="1" position="1"/>
  <ShotRow row="3" col="4" bit="1" position="2"/>
  <ShotRow row="3" col="5" bit="1" position="3"/>
  <ShotRow row="3" col="6" bit="1" position="4"/>
  <ShotRow row="3" col="7" bit="1" position="5"/>
  <ShotRow row="3" col="8" bit="1" position="6"/>
  <ShotRow row="3" col="9" bit="1" position="7"/>
  <ShotRow row="3" col="10" bit="1" position="8"/>
  <ShotRow row="3" col="11" bit="1" position="9"/>
</ShotRows>

输出1:

<ShotRows>
  <ShotRow row="3" colStart="3" colEnd="11" />
</ShotRows>
<!-- because the col is continuous from 3 to 11 -->

示例2:

<ShotRows>
  <ShotRow row="3" col="3" bit="1" position="1"/>
  <ShotRow row="3" col="4" bit="1" position="2"/>
  <ShotRow row="3" col="6" bit="1" position="3"/>
  <ShotRow row="3" col="7" bit="1" position="4"/>
  <ShotRow row="3" col="8" bit="1" position="5"/>
  <ShotRow row="3" col="10" bit="1" position="6"/>
  <ShotRow row="3" col="11" bit="1" position="7"/>
  <ShotRow row="3" col="15" bit="1" position="8"/>
  <ShotRow row="3" col="19" bit="1" position="9"/>
</ShotRows>

输出2:

<ShotRows>
  <ShotRow row="3" colStart="3" colEnd="4" />
  <ShotRow row="3" colStart="6" colEnd="8" />
  <ShotRow row="3" colStart="10" colEnd="11" />
  <ShotRow row="3" colStart="15" colEnd="15" />
  <ShotRow row="3" colStart="19" colEnd="19" />
</ShotRows>

基本思想是将任何连续的col分组为一个元素,例如col 3到4,col 6到8,col 10到11,col 15只有一个,col 19只有一个。提前谢谢。

3 个答案:

答案 0 :(得分:2)

使用Java,您可以使用Saxon 9和XSLT 2.0,如下所示:

<xsl:stylesheet
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  version="2.0">

  <xsl:output indent="yes"/>

  <xsl:template match="ShotRows">
    <xsl:copy>
      <xsl:for-each-group select="ShotRow" group-adjacent="number(@col) - position()">
        <ShotRow row="{@row}" colStart="{@col}" colEnd="{@col + count(current-group()) - 1}"/>
      </xsl:for-each-group>
    </xsl:copy>
  </xsl:template>

</xsl:stylesheet>

答案 1 :(得分:1)

使用精心设计的XPath表达式,这是一个简单的选择和复制操作。

<xsl:stylesheet 
  version="1.0" 
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
>
  <xsl:template match="ShotRows">
    <xsl:copy>
      <xsl:apply-templates select="ShotRow[
        not(preceding-sibling::ShotRow) 
        or 
        not(@col = preceding-sibling::ShotRow[1]/@col + 1)
      ]" />
    </xsl:copy>
  </xsl:template>

  <xsl:template match="ShotRow">
    <xsl:copy>
      <xsl:copy-of select="@row" />
      <xsl:attribute name="colStart">
        <xsl:value-of select="@col" />
      </xsl:attribute>
      <xsl:attribute name="colEnd">
        <xsl:value-of select="(. | following-sibling::ShotRow)[
          not(@col = following-sibling::ShotRow[1]/@col - 1)
        ][1]/@col" />
      </xsl:attribute>
    </xsl:copy>
  </xsl:template>

</xsl:stylesheet>

生成您要求的完全相同的输出。第一个XPath表达式是:

ShotRow[
  not(preceding-sibling::ShotRow) 
  or 
  not(@col = preceding-sibling::ShotRow[1]/@col + 1)
]

并选择

的所有<ShotRow>个节点
  • 要么没有前任,要么是集合中的第一个(或唯一的)
  • 或他们的@col并不比他们各自的前任
  • 多一个
  • ergo:这些条件表示连续范围的开始
  • 我已通过#s以下
  • 标记了所有这些位置

第二个表达更微妙一点:

(. | following-sibling::ShotRow)[
  not(@col = following-sibling::ShotRow[1]/@col - 1)
][1]/@col
  • (. | following-sibling::ShotRow)是当前节点和所有后续兄弟节点的联合 - 我会使用“follow-sibling-or-self”,但不幸的是这样的轴不存在;)
  • 这些节点,它选择那些@col不比其各自后继者少一个的
  • ergo:此条件表示连续范围的结束(请注意,这会选择前面任何连续范围的所有结尾)
  • 这些节点的
  • ,它取第一个(我们对“连续范围的第一端”或最接近我们的一个感兴趣)
  • 我已通过#e以下
  • 标记了所有这些位置

你的例子:

<ShotRows>
  <ShotRow row="3" col="3" bit="1" position="1"/><!-- #s -->
  <ShotRow row="3" col="4" bit="1" position="2"/><!-- #e -->
  <ShotRow row="3" col="6" bit="1" position="3"/><!-- #s -->
  <ShotRow row="3" col="7" bit="1" position="4"/>
  <ShotRow row="3" col="8" bit="1" position="5"/><!-- #e -->
  <ShotRow row="3" col="10" bit="1" position="6"/><!-- #s -->
  <ShotRow row="3" col="11" bit="1" position="7"/><!-- #e -->
  <ShotRow row="3" col="15" bit="1" position="8"/><!-- #s #e -->
  <ShotRow row="3" col="19" bit="1" position="9"/><!-- #s #e -->
</ShotRows>

输出:

<ShotRows>
  <ShotRow row="3" colStart="3" colEnd="4" />
  <ShotRow row="3" colStart="6" colEnd="8" />
  <ShotRow row="3" colStart="10" colEnd="11" />
  <ShotRow row="3" colStart="15" colEnd="15" />
  <ShotRow row="3" colStart="19" colEnd="19" />
</ShotRows>

编辑 - 上面的修改版本使用XSL密钥。对于大型输入文档,性能提升应该变得明显,主要是因为'kEnd'减少了处理时间。 'kStart'没有太大的影响,我只把它包含在代码对称中。

<xsl:stylesheet 
  version="1.0" 
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
>
  <xsl:key 
    name="kStart" 
    match="ShotRow[
      not(preceding-sibling::ShotRow) 
      or 
      not(@col = preceding-sibling::ShotRow[1]/@col + 1)
    ]" 
    use="generate-id(..)" 
  />
  <xsl:key 
    name="kEnd" 
    match="ShotRow[
      (. | following-sibling::ShotRow)[
        not(@col = following-sibling::ShotRow[1]/@col - 1)
      ]
    ]" 
    use="concat(generate-id(..), ':', generate-id())" 
  />

  <xsl:template match="ShotRows">
    <xsl:copy>
      <xsl:apply-templates select="key('kStart', generate-id(.))" />
    </xsl:copy>
  </xsl:template>

  <xsl:template match="ShotRow">
    <xsl:copy>
      <xsl:copy-of select="@row" />
      <xsl:attribute name="colStart">
        <xsl:value-of select="@col" />
      </xsl:attribute>
      <xsl:attribute name="colEnd">          
        <xsl:value-of select="key('kEnd', 
          concat(generate-id(..), ':', generate-id())
        )[1]/@col" />
      </xsl:attribute>
    </xsl:copy>
  </xsl:template>

</xsl:stylesheet>

逻辑与上面解释的完全相同。

答案 2 :(得分:0)

这感觉有点混乱,因为迭代处理通常在XSLT中进行。

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

    <xsl:output method="xml" indent="yes" />

    <xsl:template match="ShotRows">
        <xsl:copy>
            <xsl:apply-templates select="ShotRow[1]" />
        </xsl:copy>
    </xsl:template>

    <xsl:template match="ShotRow">
        <xsl:call-template name="ShotRow">
            <xsl:with-param name="start" select="@col" />
            <xsl:with-param name="shotrow" select="." />
        </xsl:call-template>
    </xsl:template>

    <xsl:template name="ShotRow">
        <xsl:param name="start" />
        <xsl:param name="shotrow" />

        <xsl:choose>
            <xsl:when test="$shotrow/@row = $shotrow/following-sibling::ShotRow[1]/@row and 1 + number($shotrow/@col) = number($shotrow/following-sibling::ShotRow[1]/@col)">
                <xsl:call-template name="ShotRow">
                    <xsl:with-param name="start" select="$start" />
                    <xsl:with-param name="shotrow" select="$shotrow/following-sibling::ShotRow[1]" />
                </xsl:call-template>

            </xsl:when>
            <xsl:otherwise>
                <ShotRow row="{$shotrow/@row}" colStart="{$start}" colEnd="{$shotrow/@col}" />
                <xsl:apply-templates select="$shotrow/following-sibling::ShotRow[1]" />

            </xsl:otherwise>
        </xsl:choose>

    </xsl:template>

</xsl:stylesheet>