【问题标题】:Wrap multiple optional XML elements into a new wrapper XML element using XSLT使用 XSLT 将多个可选 XML 元素包装到一个新的包装器 XML 元素中
【发布时间】:2011-02-07 18:51:32
【问题描述】:

我有一个如下所示的 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>

我想将元素 &lt;d&gt;&lt;e&gt; 包装到 &lt;wrapper&gt; 元素中,所以我可以有如下内容:

<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 的新手)是 &lt;d&gt;&lt;e&gt; 都是可选的。

我该怎么做?

【问题讨论】:

  • 好问题,+1。请参阅我的完整答案,使用最基础和最强大的 XSLT 设计模式:身份规则的使用和覆盖。
  • 为了正确定义这个分组问题,我认为你应该澄清元素是否必须是下一个兄弟姐妹(组相邻)或顺序不计数(按模式分组)。

标签: xml xslt


【解决方案1】:

这种转变

<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 更好的答案。这将所有兄弟姐妹分组在第一个的位置。我会使用xsl:call-template name="identity" 作为mode="copy" 内容模板,以便在任何层次结构级别进行分组。
  • 不错的答案。不幸的是,虽然它创建了我想要的层次结构(使用额外的包装元素),但它也保留了原始元素。所以,我得到了 ....,然后我得到了 ......。这有什么原因吗?我还应该指出,我实际上在包装器下面还有一个元素(类似于 ....),但我相应地更新了您的解决方案。
  • @Alex:我总是在发布答案之前测试我的代码。当应用于您的 XML 文档时,转换会准确地产生呈现的结果。此结果不包含无关的 de 元素。这些在您看来的唯一原因可能是您修改了解决方案(不了解其原理)。
  • @Dimitre:哦,其实我应该道歉。我说话时没有先彻底检查这个问题。你是绝对正确的!代码运行良好!非常感谢。
【解决方案2】:

因为Dimitre发了group by pattern解,所以我就跟group by相邻解,以防万一:

<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>

EDIT:更短的命名模板。

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

【讨论】:

    【解决方案3】:

    你不能简单地围绕它进行一个 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>
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-03-30
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多