使用xslt复制和重新排列XML节点

时间:2013-11-29 15:52:21

标签: xml xslt nodes move

我有以下XML结构:

<root>
    <product>
        <name>  
            <book1>data for book1</book1>
            <book12>data for book12</book12>
            <book13>data for book13</book13>
            <book14>data for book14</book14>
        </name>
        <info>
            <data1>data for data1</data1>
            <data2>data for data2</data2>
            <data3>data for data3<data3>
            <data4>data for data4</data4>
            <data5>data for data5</data5>
            <data n+1>data n+1</data n+1>
        </info>

        <pictures>
            <pic1>data for pic1</pic1>
            <pic2>data for pic2</pic2>
            <pic3>data for pic3</pic3>
            <pic4>data for pic4</pic4>
            <pic n+1>data for pic n+1</pic n+1>
        </pictures>

    </product>

    <product>
    .
    .
    .
    .
    .
    </product>
    .
    .
    .
    .
</root>

现在我需要在每个节点产品中复制data5,book14和整个节点图片,因此输出xml将如下所示:

<root>
    <product>
        <node_that_i_would_name_1>
          <node_that_i_would_name_2>
            <node_that_i_would_name_3>
                <node_that_i_would_name_4>
                    <book14>data for book14</book14>
                </node_that_i_would_name_4>             
                <node_that_i_would_name_5>
                    <data5>data for data5</data5>
                </node_that_i_would_name_5>     
                <picturesA>
                    <pic1A>
                    <pic2A>
                    <pic3A>
                    <pic4A>
                    <pic n+1A>
                </picturesA>
                <empty_node_at_the_end_that_i_will_name></empty_node_at_the_end_that_i_will_name>
            </node_that_i_would_name_3>
          </node_that_i_would_name_2>
        </node_that_i_would_name_1>
        <name>  
            <book1>data for book1</book1>
            <book12>data for book12</book12>
            <book13>data for book13</book13>
            <book14>data for book14</book14>
        </name>
        <info>
            <data1>data for data1</data1>
            <data2>data for data2</data2>
            <data3>data for data3<data3>
            <data4>data for data4</data4>
            <data5>data for data5</data5>
            <data n+1>data n+1</data n+1>
        </info>

        <pictures>
            <pic1>data for pic1</pic1>
            <pic2>data for pic2</pic2>
            <pic3>data for pic3</pic3>
            <pic4>data for pic4</pic4>
            <pic n+1>data for pic n+1</pic n+1>
        </pictures>

    </product>

    <product>
    .
    .
    .
    .
    .
    </product>
    .
    .
    .
    .
</root>

每件事都应该保持不变,只有这些数据必须以上面的方式复制和重新规划到节点中。有没有简单的方法用xslt做到这一点?

1 个答案:

答案 0 :(得分:3)

当您看到这样的问题时,您的第一个想法应该是Identity Transform

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

所有这一切本身就是将所有现有节点复制到输出文档。但这意味着您只需为要转换的任何特定元素编写模板,在您的情况下为 product 元素。 (XSLT将优先考虑与特定节点名称相匹配的模板,而不是身份模板使用的更通用的匹配项。)

因此,产品的模板将具有此结构。从本质上讲,它仍然是身份转换,但会引入额外的代码。

<xsl:template match="product">
   <xsl:copy>
      <xsl:apply-templates select="@*" />
      <!-- Code to create new nodes and create extra copies of existing ones goes here -->
      <xsl:apply-templates select="node()"/>
  </xsl:copy>

编辑:请注意,需要在任何元素之前复制属性!

例如,要在新元素中创建 book14 元素的副本,只需添加以下代码:

 <newnode1>
   <xsl:apply-templates select="name/book14"/>
 </newnode1>  

对于Picture元素,要允许使用元素名称上的额外后缀复制它,您将拥有一个与子元素匹配的模板,以及一个(可选)参数,该参数随后将用于创建元素名称

<xsl:template match="pictures/*">
   <xsl:param name="suffix" />
   <xsl:element name="{concat(local-name(), $suffix)}">
      <xsl:apply-templates select="@*|node()"/>
   </xsl:element>
</xsl:template>

然后,该模板将在两个不同的地方使用。通常与结构匹配的地方,以及您想要创建额外副本的地方。但在后一种情况下,您可以设置参数:

<xsl:apply-templates select="pictures/*">
   <xsl:with-param name="suffix" select="'A'" />
</xsl:apply-templates>

尝试将此XSLT作为示例,您应该可以使用那些您真正想要自己命名的节点进行扩展....

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

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

   <xsl:template match="product">
      <xsl:copy>
         <xsl:apply-templates select="@*"/>
         <newnode>
            <newnode1>
               <xsl:apply-templates select="name/book14"/>
            </newnode1>
            <newnode2>
               <xsl:apply-templates select="info/data5"/>
            </newnode2>
            <picturesA>
               <xsl:apply-templates select="pictures/*">
                  <xsl:with-param name="suffix" select="'A'" />
               </xsl:apply-templates>
            </picturesA>
         </newnode>
         <xsl:apply-templates select="node()"/>
      </xsl:copy>
   </xsl:template>

   <xsl:template match="pictures/*">
      <xsl:param name="suffix" />
      <xsl:element name="{concat(local-name(), $suffix)}">
         <xsl:apply-templates select="@*|node()"/>
      </xsl:element>
   </xsl:template>
</xsl:stylesheet>