使用XSLT将多个可选XML元素包装到新的包装器XML元素中

时间:2011-02-07 18:51:33

标签: xml xslt

我有一个XML文件,如下所示:

<a>
  ......
  <b>
    <c>
      <c1>some text</c1>
    </c>
    <d>
      <d1>some more text</d1>
      <d1>even more text</d1>
    </d>
    <e>
      <e1>some more text</e1>
      <e1>even more text</e1>
    </e>
  </b>
</a>

我想将元素<d><e>包装到<wrapper>元素中,所以我可以使用以下内容:

<a>
  ......
  <b>
    <c>
      <c1>some text</c1>
    </c>
    <wrapper>
      <d>
        <d1>some more text</d1>
        <d1>even more text</d1>
      </d>
      <e>
        <e1>some more text</e1>
        <e1>even more text</e1>
        <e2>even more</e2>
      </e>
    </wrapper>
  </b>
  ......
</a>

我面临的一个问题(除了我是XSLT新手的事实)是<d><e>都是可选的。

我该怎么做?

3 个答案:

答案 0 :(得分:2)

此转化

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

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

 <xsl:template match="b/*[self::d or self::e][1]">
  <wrapper>
    <xsl:apply-templates mode="copy" select=
    ". | following-sibling::*[self::d or self::e]"/>
  </wrapper>
 </xsl:template>

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

 <xsl:template match="b/*[self::d or self::e][position()>1]"/>
</xsl:stylesheet>

应用于提供的XML文档

<a>   ......   
    <b>
        <c>
            <c1>some text</c1>
        </c>
        <d>
            <d1>some more text</d1>
            <d1>even more text</d1>
        </d>
        <e>
            <e1>some more text</e1>
            <e1>even more text</e1>
        </e>
    </b>
</a>

产生想要的结果

<a>   ......   
    <b>
        <c>
            <c1>some text</c1>
        </c>
        <wrapper>
            <d>
                <d1>some more text</d1>
                <d1>even more text</d1>
            </d>
            <e>
                <e1>some more text</e1>
                <e1>even more text</e1>
            </e>
        </wrapper>
    </b>
</a>

注意:覆盖身份规则+模式。

答案 1 :(得分:2)

因为Dimitre通过模式解决方案发布组,我将通过相邻解决方案与组进行,以防万一:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:strip-space elements="*"/>
    <xsl:template match="node()|@*">
        <xsl:copy>
            <xsl:apply-templates select="node()[1]|@*"/>
        </xsl:copy>
        <xsl:apply-templates select="following-sibling::node()[1]"/>
    </xsl:template>
    <xsl:template match="*[self::d|self::e][1]">
        <wrapper>
            <xsl:call-template name="group"/>
        </wrapper>
        <xsl:apply-templates select="following-sibling::node()
                                        [not(self::d|self::e)][1]"/>
    </xsl:template>
    <xsl:template match="node()" mode="group" name="group">
        <xsl:copy>
            <xsl:apply-templates select="node()[1]|@*"/>
        </xsl:copy>
        <xsl:apply-templates select="following-sibling::node()[1]
                                                [self::d|self::e]"
                             mode="group"/>
    </xsl:template>
</xsl:stylesheet>

输出:

<a>   ......   
    <b>
        <c>
            <c1>some text</c1>
        </c>
        <wrapper>
            <d>
                <d1>some more text</d1>
                <d1>even more text</d1>
            </d>
            <e>
                <e1>some more text</e1>
                <e1>even more text</e1>
            </e>
        </wrapper>
    </b>
</a>

编辑:更短的命名模板。

注意:这会在任何层次结构级别按原始顺序对下一个兄弟分组。这意味着它可能是几个wrapper元素。

答案 2 :(得分:0)

难道你不能简单地围绕它进行if测试吗?

<wrapper>
    <xsl:if test="/a/b/d">
    <!-- write out the <d> node -->
    </xsl:if>
    <xsl:if test="/a/b/e">
    <!-- write out the <e> node -->
    </xsl:if>
</wrapper>