如何使用xslt合并两个同级

时间:2019-02-22 11:13:48

标签: xml xslt

我需要合并两个兄弟姐妹

<table>
    <tbody>
    <tr>
    <td> table data</td>
    </tr>
    </tbody>
    <tbody>
    <tr>
    <td> table data</td>
    </tr>
    </tbody>
    </table>

预期输出:

<table>
<tbody>
<tr>
<td> table data</td>
</tr>
<tr><td> table data</td>
</tr>
</tbody>
</table>

我的xslt代码是:

<xsl:template match="/">
   <xsl:for-each-group select="*" group-adjacent="boolean(self::tbody)">
         <tbody>
    <xsl:value-of select="."/>
         </tbody>
         </xsl:for-each-group>
    </xsl:template>

没有给出正确的输出。你能建议吗

2 个答案:

答案 0 :(得分:1)

您当前的XSLT存在很多问题

  1. 您的模板与document节点匹配,在您的情况下,该节点是table元素的父级,而不是table元素本身。当您尝试对子tbody元素进行分组时,您的模板应与table元素匹配
  2. xsl:value-of仅返回节点的文本值。您应该在此处使用xsl:copy-of(或将xsl:apply-templates与身份模板结合使用)。您还应该选择组中的所有项目,而不仅仅是当前项目。
  3. 您尚未考虑tbody以外的表下节点的情况

因此,您的XSLT应该如下所示。...

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
  <xsl:output method="html" indent="yes" />
  <xsl:strip-space elements="*" />

  <xsl:template match="@*|node()">
    <xsl:copy>
      <xsl:apply-templates select="@*|node()"/>
    </xsl:copy>
  </xsl:template>

  <xsl:template match="table">
    <table>
      <xsl:for-each-group select="*" group-adjacent="boolean(self::tbody)">
        <xsl:choose>
          <xsl:when test="current-grouping-key()">
            <tbody>
              <xsl:apply-templates select="current-group()/*"/>
            </tbody>
          </xsl:when>
          <xsl:otherwise>
            <xsl:apply-templates select="current-group()" />
          </xsl:otherwise>
        </xsl:choose>
      </xsl:for-each-group>
    </table>
  </xsl:template>
</xsl:stylesheet>

注意,如果您使用的是XSLT 3.0,则可以用以下内容替换身份模板:

<xsl:mode on-no-match="shallow-copy"/>

另一方面,如果您实际上只使用XSLT 1.0,则需要执行Muenchian Grouping。这意味着要定义一个键,如下所示:

<xsl:key name="table" match="table/*" use="boolean(self::tbody)" />

然后,不要使用xsl:for-each-group,而是这样做(尽管这将对所有tbody元素进行分组,而不仅仅是相邻的元素)

<xsl:for-each select="*[generate-id() = generate-id(key('table', boolean(self::tbody))[1])]">

尝试使用此XSLT

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
  <xsl:output method="html" indent="yes" />
  <xsl:strip-space elements="*" />

  <xsl:key name="table" match="table/*" use="boolean(self::tbody)" />

  <xsl:template match="@*|node()">
    <xsl:copy>
      <xsl:apply-templates select="@*|node()"/>
    </xsl:copy>
  </xsl:template>

  <xsl:template match="table">
    <table>
      <xsl:for-each select="*[generate-id() = generate-id(key('table', boolean(self::tbody))[1])]">
        <xsl:choose>
          <xsl:when test="self::tbody">
            <tbody>
              <xsl:apply-templates select="key('table', true())/*"/>
            </tbody>
          </xsl:when>
          <xsl:otherwise>
            <xsl:apply-templates select="key('table', false())" />
          </xsl:otherwise>
        </xsl:choose>
      </xsl:for-each>
    </table>
  </xsl:template>
</xsl:stylesheet>

当然,毕竟,在这种情况下,Michael.Hor257k的答案要简单得多。 (尽管如果您确实坚持使用XSLT 1.0,那么绝对值得阅读Muenchian Grouping)。

答案 1 :(得分:0)

您的示例可以通过以下方式简单处理:

XSLT 1.0

<xsl:stylesheet version="1.0" 
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>

<xsl:template match="/table">
    <xsl:copy>
        <tbody>
            <xsl:copy-of select="tbody/*"/>
        </tbody>
    </xsl:copy>
</xsl:template>

</xsl:stylesheet>