【问题标题】:rearrange xml nodes including sub-nodes by xslt通过 xslt 重新排列 xml 节点,包括子节点
【发布时间】:2011-11-29 02:56:34
【问题描述】:

我有一个 xml 文档,现在我想将它翻译成另一个内容相同但元素顺序不同的 xml 文档。

原始的xml文档如:

<?xml version = "1.0" encoding = "UTF-8"?>  
<order xmlns:xsi = "http://www.w3.org/2001/XMLSchema-instance" >  
 <ship>  
    <zipcode>78712</zipcode>  
    <street>1234 Main Street</street>  
    <country>CN</country>    
    <city>Beijing</city>  
 </ship>   
 <items>     
    <quantity>1</quantity>     
    <itemno>1234</itemno>  
 </items>     
 <items>     
    <quantity>3</quantity>    
    <itemno>1235</itemno>    
 </items>    
 <price>456</price>  
 <customer>Tom Hill</customer>    
</order>  

预期的输出 xml 文档如:

<?xml version = "1.0" encoding = "UTF-8"?>  
<order xmlns:xsi = "http://www.w3.org/2001/XMLSchema-instance" >  
 <customer>Tom Hill</customer>    
 <ship>  
    <street>1234 Main Street</street>  
    <city>Beijing</city>  
    <zipcode>78712</zipcode>  
    <country>CN</country>    
 </ship>    
 <items>     
    <itemno>1234</itemno>    
    <quantity>1</quantity>     
 </items>     
 <items>     
    <itemno>1235</itemno>    
    <quantity>3</quantity>    
 </items>    
 <price>456</price>  
</order> 

我使用了下面的xslt文档来翻译它。

<?xml version="1.0"?>  
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">  
<xsl:template match="/order">  
 <xsl:copy>  
  <xsl:copy-of select="customer" />  
  <xsl:copy-of select="ship" >  
  <xsl:call-template name="TShip" />  
  </xsl:copy-of>  
  <xsl:copy-of select="items">  
  <xsl:call-template name="TItems" />  
  </xsl:copy-of>  
 <xsl:copy-of select="price" />  
 </xsl:copy>  
</xsl:template>  

<xsl:template name="TShip">  
 <xsl:copy>  
  <xsl:copy-of select="street" />  
  <xsl:copy-of select="city" />  
  <xsl:copy-of select="zipcode" />  
  <xsl:copy-of select="country" />  
 </xsl:copy>  
</xsl:template>  

<xsl:template name="TItems">  
 <xsl:for-each select="items">  
  <xsl:copy>  
   <xsl:copy-of select="itemno" />  
   <xsl:copy-of select="quantity" />  
  </xsl:copy>  
 </xsl:for-each>  
</xsl:template>  

</xsl:stylesheet>  

但是,翻译结果不是我的预期。 翻译结果xml:

<?xml version = "1.0" encoding = "UTF-8"?>  
<order xmlns:xsi = "http://www.w3.org/2001/XMLSchema-instance" >  
 <customer>Tom Hill</customer>    
 <ship>  
    <zipcode>78712</zipcode>  
    <street>1234 Main Street</street>  
    <country>CN</country>    
    <city>Beijing</city>    
 </ship>    
 <items>     
    <quantity>1</quantity>     
    <itemno>1234</itemno>    
 </items>     
 <items>     
    <quantity>3</quantity>    
    <itemno>1235</itemno>   
 </items>    
 <price>456</price>  
</order>  

它只是按照预期的顺序制作了第一级节点。所有子节点都保持原始顺序。我怎样才能使所有节点的顺序符合我的预期?

【问题讨论】:

    标签: xml xslt


    【解决方案1】:

    xsl:copy-of 也复制所有子节点,并且不评估它的子节点。

    因此,您的 TShip 和 TItems 模板甚至从未被评估过。 &lt;xsl:copy-of select="ship"&gt; 复制所有 &lt;ship&gt;...&lt;/ship&gt;

    对您的模板的这种修改将证明您的 TShip 和 TItems 模板没有被调用。

    <?xml version="1.0"?>  
    <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">  
    <xsl:template match="/order">  
     <xsl:copy>  
      <xsl:copy-of select="customer" />
        <xsl:copy-of select="ship">
      <xsl:call-template name="TShip" />  
    </xsl:copy-of>
      <xsl:copy-of select="items">  
      <xsl:call-template name="TItems" />  
      </xsl:copy-of>  
     <xsl:copy-of select="price" />  
     </xsl:copy>  
    </xsl:template>  
    
    <xsl:template name="TShip">  
     <xsl:copy>  
      <test>TShip called</test>
      <xsl:copy-of select="street" />  
      <xsl:copy-of select="city" />  
      <xsl:copy-of select="zipcode" />  
      <xsl:copy-of select="country" />  
     </xsl:copy>  
    </xsl:template>  
    
    <xsl:template name="TItems">  
     <xsl:for-each select="items">  
      <xsl:copy> 
      <test>TItems called</test>
       <xsl:copy-of select="itemno" />  
       <xsl:copy-of select="quantity" />  
      </xsl:copy>  
     </xsl:for-each>  
    </xsl:template>  
    
    </xsl:stylesheet>
    

    请注意,输出确实包含我添加的&lt;test&gt; 元素。

    你需要做的是递归隐式复制。通常xsl:copyxsl:copy-ofxsl:for-each 是不良xsl 模板设计的标志——很少有问题是xsl:templatexsl:apply-template 不能更好地处理身份转换。

    我会这样做:

    <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    
        <xsl:output encoding="UTF-8" indent="yes" method="xml" />
    
        <xsl:template match="order">
            <xsl:copy>
                <!-- copy all attributes; maybe you don't want this -->
                <xsl:apply-templates select="@*" />
                <!-- copy some elements in a specific order  -->
                <xsl:apply-templates select="customer" />
                <xsl:apply-templates select="ship" />
                <xsl:apply-templates select="items" />
                <xsl:apply-templates select="price" />
                <!-- now copy any other children that we haven't explicitly reordered; again, possibly this is not what you want -->
                <xsl:apply-templates select="*[not(self::customer or self::ship or self::items or self::price)]"/>
            </xsl:copy>
        </xsl:template>
    
        <xsl:template match="ship">
            <xsl:copy>
                <xsl:apply-templates select="@*" />
                <xsl:apply-templates select="street" />
                <xsl:apply-templates select="city" />
                <xsl:apply-templates select="zipcode" />
                <xsl:apply-templates select="country" />
                <xsl:apply-templates select="*[not(self::street or self::city or self::zipcode or self::country)]"/>
            </xsl:copy>
        </xsl:template>
    
        <xsl:template match="items">
            <xsl:copy>
                <xsl:apply-templates select="@*" />
                <xsl:apply-templates select="itemno" />
                <xsl:apply-templates select="quantity" />
                <xsl:apply-templates select="*[not(self::itemno or self::quantity)]"/>
            </xsl:copy>
        </xsl:template>
    
        <!-- this is the identity transform: it copies everything that isn't matched by a more specific template -->
        <xsl:template match="@*|node()">
            <xsl:copy>
                <xsl:apply-templates select="@*|node()"/>
            </xsl:copy>
        </xsl:template>
    
    </xsl:stylesheet>
    

    请注意此模板设计对源 XML 的结构所做的假设减少了多少。它也更容易更改:例如,如果您想静音或重命名可能本身有子元素的特定元素,您只需添加一个与该元素匹配的新 xsl:template,做任何您需要做的事情,然后 @987654334 @对孩子们。

    您应该learn more about this XSLT pattern,因为它非常通用,可以让模板创作变得不那么乏味,您的模板也不那么脆弱。

    【讨论】:

    • 你的回答救了我!谢谢。
    【解决方案2】:

    我怎样才能使所有节点的顺序符合我的预期?

    简短回答:使用&lt;xsl:apply-templates/&gt;&lt;xsl:template&gt; 而不是&lt;xsl:copy-of&gt;


    这是一个完整的转换

    <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()|@*">
      <xsl:copy>
       <xsl:apply-templates select="node()|@*"/>
      </xsl:copy>
     </xsl:template>
    
     <xsl:template match="order">
       <xsl:copy>
        <xsl:apply-templates select="customer"/>
        <xsl:apply-templates select="*[not(self::customer)]"/>
       </xsl:copy>
     </xsl:template>
    
     <xsl:template match="ship">
      <xsl:copy>
       <xsl:apply-templates select="street"/>
       <xsl:apply-templates select="city"/>
       <xsl:apply-templates select="zipcode"/>
       <xsl:apply-templates select="country"/>
      </xsl:copy>
     </xsl:template>
    
     <xsl:template match="items">
      <xsl:copy>
       <xsl:apply-templates select="itemno"/>
       <xsl:apply-templates select="quantity"/>
      </xsl:copy>
     </xsl:template>
    </xsl:stylesheet>
    

    当此转换应用于提供的 XML 文档时

    <order xmlns:xsi = "http://www.w3.org/2001/XMLSchema-instance" >
        <ship>
            <zipcode>78712</zipcode>
            <street>1234 Main Street</street>
            <country>CN</country>
            <city>Beijing</city>
        </ship>
        <items>
            <quantity>1</quantity>
            <itemno>1234</itemno>
        </items>
        <items>
            <quantity>3</quantity>
            <itemno>1235</itemno>
        </items>
        <price>456</price>
        <customer>Tom Hill</customer>
    </order>
    

    产生想要的正确结果

    <order xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
       <customer>Tom Hill</customer>
       <ship>
          <street>1234 Main Street</street>
          <city>Beijing</city>
          <zipcode>78712</zipcode>
          <country>CN</country>
       </ship>
       <items>
          <itemno>1234</itemno>
          <quantity>1</quantity>
       </items>
       <items>
          <itemno>1235</itemno>
          <quantity>3</quantity>
       </items>
       <price>456</price>
    </order>
    

    解释

    <xsl:copy-of select="someElement"/>
    

    复制someElement 为根的整个子树完全按原样(如果我们有一条重新排列后代的指令,这条指令怎么知道我们想要的顺序???)。

    为了改变任何同级元素的顺序 - 元素,我们必须指定新的、想要的顺序。

    这可以通过编写一系列&lt;xsl:apply-templates&gt; 指令来完成,每个指令都以所需的顺序选择所需的元素。我们可以编写&lt;xsl:copy-of&gt; 指令,但仅用于复制元素,我们希望其后代保持原始顺序。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2015-12-05
      • 1970-01-01
      • 1970-01-01
      • 2017-01-13
      • 2019-11-10
      • 1970-01-01
      • 2021-03-21
      • 1970-01-01
      相关资源
      最近更新 更多