2011-11-29 130 views
4

我有一個xml文檔,現在我想將它翻譯成具有相同內容但不同元素順序的另一個xml文檔。重新排列包含子節點的xml節點by xslt

原始的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> 

它只是取得了預期的順序中的第一級節點。所有的子節點都保持原來的順序。我如何按照我的預期製作所有節點的順序?

回答

9

xsl:copy-of複製所有子節點,並且它的子節點不被評估。

因此,您的TShip和TItems模板從未被評估過。<xsl:copy-of select="ship">複製全部<ship>...</ship>

此修改你的模板將證明你的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> 

注意輸出確實包含<test>元件我加入。

你需要做什麼,而不是是遞歸的隱含複製。通常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,做任何你需要做的事情,然後xsl:apply-templates對孩子。

你應該learn more about this XSLT pattern,因爲它是非常靈活的,使模板創作少得多乏味,你的模板更容易脆弱。

+0

這是完美的,謝謝你的指導。 – zgcharley

1

我如何使所有節點的順序如我所料?

簡短的回答:通過使用<xsl:apply-templates/><xsl:template>代替<xsl:copy-of>


這是一個完整的轉型

<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根的整個子樹正是因爲是(如果我們有一條重新安排子孫的指令,這條指令如何知道我們想要的順序?)。

爲了改變任何兄弟姐妹的順序 - 元素,我們必須指定新的想要的順序。

這可以通過編寫一個<xsl:apply-templates>指令序列來完成,每個指令都按照所需的順序選擇所需的元素。我們可以編寫<xsl:copy-of>指令,但僅限於複製要素,其後代我們希望保持其原始順序。

+0

感謝您的回覆 – zgcharley

+0

@zgcharley:不客氣。 –