2013-02-17 21 views
0

我想知道如何使用xsl模板將我的xml文檔轉換爲具有原始元素層次結構的另一個xml文檔我還想爲新生成的XML中的元素添加一些屬性。如何使用xsl模板和遞歸構建一個新的xml文檔?

我原來的XML文件看起來是這樣的:

<shop> 
    <product> 
    <cookie ID="001"> 
    <price>2</price> 
    </cookie> 
    </product> 

    <product> 
    <bread ID="002"> 
    <price>5</price> 
    </bread> 
    </product> 

    <product> 
    <milk ID="003"> 
    <price>2</price> 
    </milk> 
    </product> 
</shop> 

我想這個轉換爲下面的XML:

<newXML> 
    <newElement> 
    <newElement ID="001"> 
    <newElement price="2"/> 
    </newElement> 
    </newElement> 

    <newElement> 
    <newElement ID="002"> 
    <newElement price="5"/> 
    </newElement> 
    </newElement> 

    <newElement> 
    <newElement ID="003"> 
    <newElement price="2"/> 
    </newElement> 
    </newElement> 
</newXML> 

什麼是做到這一點的好辦法?這可以通過模板遞歸來完成,還是有更好的方法?我一直在嘗試使用以下邏輯:

  1. 使創建元素
  2. 閱讀潮流元素ID的模板,如果它存在,並把它爲newElement
  3. 如果當前元素有一個孩子,這個應用模板(某種遞歸)

儘管進行了很多嘗試,我還是無法完成這項工作。您的幫助將不勝感激!

回答

2

爲此,您將基於XSLT標識轉換進行構建,該轉換是XSLT中非常常見的設計模式。首先你只是有一個模板,複製的元素,準確地把它們作爲-是]

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

結果,你最後要做的就是添加額外的模板來匹配您的元素,並添加代碼,以將其重命名,或添加屬性按要求。例如,要重命名根元素,您需要添加以下模板:

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

您不必在此處使用特定的元素名稱。如果你想重命名和元件都具有ID屬性相同的名稱,你可以做這樣的事情

<xsl:template match="product/*[@ID]"> 
    <newElement> 
     <xsl:apply-templates select="@*|node()"/> 
    </newElement> 
</xsl:template> 

而在你價格元素的情況下,如果你想創建一個基於屬性對元素的文本內容,您可以添加一個模板,像這樣:

<xsl:template match="price"> 
    <newElement price="{.}" /> 
</xsl:template> 

(注意,這裏採用屬性值模板在這裏創建屬性這通常是首選使用。 XSL:屬性

試試這個XML對於初學者:

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

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

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

    <xsl:template match="product/*[@ID]"> 
     <newElement> 
     <xsl:apply-templates select="@*|node()"/> 
     </newElement> 
    </xsl:template> 

    <xsl:template match="price"> 
     <newElement price="{.}" /> 
    </xsl:template> 

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

此輸出以下:

<newXML> 
    <newElement> 
     <newElement ID="001"> 
     <newElement price="2"/> 
     </newElement> 
    </newElement> 
    <newElement> 
     <newElement ID="002"> 
     <newElement price="5"/> 
     </newElement> 
    </newElement> 
    <newElement> 
     <newElement ID="003"> 
     <newElement price="2"/> 
     </newElement> 
    </newElement> 
</newXML> 
+1

非常感謝你的非常詳細的解答。所有似乎都在工作,據我測試exept 我想這個問題與「product/* [@ ID]」有關。我不斷收到以下錯誤:運行時錯誤:file transform.xsl第47行元素副本 屬性節點必須在任何子節點之前添加到元素。無論如何,你的回答非常有價值。 – Coltrane 2013-02-17 14:28:09

0

聽起來像你只是想轉換文檔,那麼爲什麼不嘗試的XQuery代替XSLT?它簡單得多..

如果您想要從輸入文件中輸入一些內容,並輸入for $variable in //xpath來重寫輸入文件與xpath表達式匹配的部分,您只需編寫要創建的xml和{..}

因此,這將創建所需的XML文件:

<newXML>{ 
    for $prod in /shop/product/* return 
    <newElement> 
     <newElement>{ 
     $prod/@ID, 
     for $value in $prod/* return 
      <newElement price="{$value}"/> 
     }</newElement> 
    </newElement> 
}</newXML>