2013-03-05 211 views
2

我正在尋找一種智能高效的XSLT,它將XML文檔轉換爲CSV數據。它應該處理子節點中所有可能的元素。 例如,XML看起來像這樣使用XSLT將XML轉換爲CSV

<?xml version="1.0" encoding="ISO-8859-1"?> 
<sObjects> 
    <sObject> 
    <Name>Raagu</Name> 
    <BillingStreet>Hoskote</BillingStreet> 
    </sObject> 
    <sObject> 
     <Name>Rajath</Name> 
     <BillingStreet>BTM</BillingStreet> 
     <age>25</age> 
    </sObject> 
    <sObject> 
     <Name>Sarath</Name> 
     <BillingStreet>Murgesh</BillingStreet> 
     <location>Bangalore</location> 
    </sObject> 
</sObjects> 

而且我出去把CSV應該是這樣的

Name,BillingStreet,age,location 
Raagu,Hoskote,, 
Rajath,BTM,25, 
Sarath,Murgesh,,Bangalore 

所有的行應該在CSV所有鍵字段,即使如果XML確實有它的價值。

以下是我通過查看這裏的不同示例創建的XSLT代碼。

這是XSLT我想出了

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 
    <xsl:output method="text"/> 
    <xsl:variable name="delimiter" select="','"/> 

    <xsl:key name="field" match="sObject/*" use="name()"/> 

    <xsl:template match="/"> 

     <xsl:for-each select="/*/*/*[generate-id()=generate-id(key('field', name())[1])]"> 
      <xsl:value-of select="name()"/> 

      <xsl:if test="position() != last()"> 
       <xsl:value-of select="$delimiter"/> 
      </xsl:if> 
     </xsl:for-each> 

     <xsl:text>&#xa;</xsl:text> 

     <xsl:for-each select="/*/sObject"> 

      <xsl:variable name="property" select="." /> 
      <xsl:for-each select="$property/*"> 

       <xsl:variable name="value" select="." /> 
       <xsl:value-of select="$value"/> 
       <xsl:if test="position() != last()"> 
        <xsl:value-of select="$delimiter"/> 
       </xsl:if> 
       <xsl:if test="position() = last()"> 
        <xsl:text>&#xa;</xsl:text> 
       </xsl:if> 

      </xsl:for-each> 

     </xsl:for-each> 


    </xsl:template> 
</xsl:stylesheet> 

,並打印出來放在

Name,BillingStreet,age,location 
Raagu,Hoskote 
Rajath,BTM,25 
Sarath,Murgesh,Bangalore 

但我想所有的行應包含那些多次值的所有鍵在第一行。

你能幫我用XSLT代碼實現嗎?

+2

我們會提供幫助。當你卡住了。嘗試一下,並詢問是否有不清楚的地方。 [常見問題] – ppeterka 2013-03-05 14:24:15

+1

可能有幾個類似問題的重複。請參閱「相關」下的鏈接。 – mzjn 2013-03-05 14:27:15

+0

謝謝ppeterka。由於我是XSLT的新手,我一直在XSLT上嘗試不同的方式來完成這個任務。我寫了示例XSL代碼來獲取第一行(所有的鍵),但沒有得到邏輯來獲取正確位置的值。我看過相關的鏈接,並沒有找到解決我的問題。我現在被困在這一點,現在需要從Stackoverflow的幫助。 – 2013-03-05 14:27:20

回答

6

這個怎麼樣了兩步解決方案

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 
    <xsl:output method="text"/> 
    <xsl:variable name="delimiter" select="','"/> 

    <xsl:key name="field" match="/*/*/*" use="local-name()"/> 

    <!-- variable containing the first occurrence of each field --> 
    <xsl:variable name="allFields" 
     select="/*/*/*[generate-id()=generate-id(key('field', local-name())[1])]" /> 

    <xsl:template match="/"> 
     <xsl:for-each select="$allFields"> 
      <xsl:value-of select="local-name()" /> 
      <xsl:if test="position() &lt; last()"> 
       <xsl:value-of select="$delimiter" /> 
      </xsl:if> 
     </xsl:for-each> 
     <xsl:text>&#10;</xsl:text> 
     <xsl:apply-templates select="*/*" /> 
    </xsl:template> 

    <xsl:template match="*"> 
     <xsl:variable name="this" select="." /> 
     <xsl:for-each select="$allFields"> 
      <xsl:value-of select="$this/*[local-name() = local-name(current())]" /> 
      <xsl:if test="position() &lt; last()"> 
       <xsl:value-of select="$delimiter" /> 
      </xsl:if> 
     </xsl:for-each> 
     <xsl:text>&#10;</xsl:text> 
    </xsl:template> 
</xsl:stylesheet> 

這裏的竅門是,allFields變量包含每個名稱的一個元素,所以它的這個我們遍歷每個節點列表行,而不僅僅是那一行中實際存在的元素。既然你說你想支持任意命名空間的XML等,我已經使用了像/*/*/*這樣的模式,而不是對任何特定的元素名稱進行硬編碼(/*/*/*只是匹配作爲文檔元素的孫子的任何元素,而不管元素名稱) ,並且我使用local-name()而不是name()忽略任何名稱空間前綴(它會將<sObject>,<sObject xmlns="foo"><f:sObject xmlns:f="foo">完全相同)。

+0

嗨伊恩 這真的很酷。感謝您的出色解決方案。 我正在平行討論'stack kind of structure'中的一些問題,它會導致O(N)時間複雜度。 我想了解從您那裏生成「allFields」變量的時間複雜度。 一旦我們生成了「allFields」變量,我希望時間複雜度爲o(n),其中'n'是allFields中字段的數量。 需要你的幫助。 此外,我想使這個XSLT更通用的地方,如果命名空間來在XML,例如 2013-03-05 15:25:43

+0

我發這樣的XML 什麼 <的sObject的xmlns = 「namespce」> raagu 我們如何處理這個問題? – 2013-03-05 15:29:28

+0

@RaghavendraNilekani,通過詢問單獨的SO問題並閱讀SO問題和答案來處理這個問題 - 類似的問題已經被提出過數千次,而且大多數都有很好的答案。另外,如果「真的很酷」,接受*答案(點擊該答案旁邊的複選標記)是一種很好的禮儀。 – 2013-03-05 15:34:51