使用XSLT将多个具有相同名称的元素包装到新元素中

时间:2014-02-06 15:14:39

标签: xml xslt

我正在尝试使用XSLT重新格式化一些XML。我输入的片段如下所示:

<toggles>
  <toggle toggleDisplayName="Charges">
    <anotherElement attribute="value" />
    <gridColumn sourceField.name="FIELD1" />
    <gridColumn sourceField.name="FIELD2" />
    <gridColumn sourceField.name="FIELD3" />
  </toggle>
</toggles>

我想将所有'gridColumn'元素包装成一个'grid'元素,如下所示:

<toggles>
  <toggle toggleDisplayName="Charges">
    <anotherElement attribute="value" />
    <grid>
      <gridColumn sourceField.name="FIELD1" />
      <gridColumn sourceField.name="FIELD2" />
      <gridColumn sourceField.name="FIELD3" />
    </grid>
  </toggle>
</toggles>

我目前的XSLT是:

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0"
     xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
     xmlns:msxsl="urn:schemas-microsoft-com:xslt"
     exclude-result-prefixes="msxsl">
  <xsl:output method="xml" indent="yes"/>

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

  <!--Identity transform for remaining-->
  <xsl:template match="@* | node()">
    <xsl:copy>
      <xsl:apply-templates select="@* | node()"/>
    </xsl:copy>
  </xsl:template>
</xsl:stylesheet>

现在这种工作,但我在每个'grid'元素中都有'gridColumn'元素。有没有简单的方法来修改它,以便我可以获得上述结果?

注意:编辑以澄清输入XML。

提前感谢您的帮助!

2 个答案:

答案 0 :(得分:1)

我能想到的最简单的变体,如果所有gridColumn元素在原始输入XML中相邻(形成一个没有其他插入元素的连续块),则该变量有效:

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

  <xsl:template match="toggle">
    <xsl:copy>
      <!-- apply templates to everything except the second and subsequent
           gridColumn child elements -->
      <xsl:apply-templates select="@* | node()[not(self::gridColumn)]
                                      | gridColumn[1]" />
    </xsl:copy>
  </xsl:template>

  <!-- this template will be called for just the first gridColumn within
       a toggle... -->
  <xsl:template match="gridColumn">
    <grid>
      <!-- ... and will gather all its sibling gridColumn elements under the
           new grid element -->
      <xsl:copy-of select="../gridColumn" />
    </grid>
  </xsl:template>

  <!--Identity transform for remaining-->
  <xsl:template match="@* | node()">
    <xsl:copy>
      <xsl:apply-templates select="@* | node()"/>
    </xsl:copy>
  </xsl:template>
</xsl:stylesheet>

如果gridColumn元素并非全部相邻,我们会发现他们已经在第一个的地方聚集到一个<grid>中em> gridColumn出现在输入XML中。

答案 1 :(得分:0)

我提出了这个解决方案。请检查它是否符合您的所有要求。

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:msxsl="urn:schemas-microsoft-com:xslt" exclude-result-prefixes="msxsl">
  <xsl:output method="xml" indent="yes" />

  <!-- Process toggle separately, because its childredn cannot simply be copied -->
  <xsl:template match="toggle">
    <xsl:copy>
      <xsl:apply-templates select="./@*" /> <!-- For some unknown reason I needed this -->
      <xsl:apply-templates />
      <grid>
        <xsl:for-each select="gridColumn">
          <xsl:copy-of select="." />
        </xsl:for-each>
      </grid>
    </xsl:copy>
  </xsl:template>

  <xsl:template match="gridColumn">
    <!-- Do nothing, because it is processed by for-each -->
  </xsl:template>

  <!--Identity transform for remaining-->
  <xsl:template match="@* | node()">
    <xsl:copy>
      <xsl:apply-templates select="@* | node()" />
    </xsl:copy>
  </xsl:template>
</xsl:stylesheet>