2011-07-11 124 views
3

這裏的源XML:XSLT轉換扁平結構數組

<customers> 
    <firstname1>Sean</firstname1> 
    <lastname1>Killer</lastname1> 
    <sex1>M</sex1> 
    <firstname2>Frank</firstname2> 
    <lastname2>Woods</lastname2> 
    <sex2>M</sex2> 
    <firstname3>Jennifer</firstname3> 
    <lastname3>Lee</lastname3> 
    <sex3>F</sex3> 
</customers> 

我怎樣才能將其轉換爲這個?

<MyCustomers> 
    <Customer> 
     <Name> Sean Killer</Name> 
     <Sex>M</Sex> 
    </Customer> 
    <Customer> 
     <Name> Frank Woods</Name> 
     <Sex>M</Sex> 
    </Customer> 
    <Customer> 
     <Name>Jennifer Lee</Name> 
     <Sex>F</Sex> 
    </Customer> 
</MyCustomers> 
+0

請使用'code'標記並過帳您的帖子。 –

+0

好問題,+1。使用XSLT 1.0查看我的答案,獲得最通用,最靈活的解決方案。即使頂層元素的子元素以任意方式重新洗牌,它也會產生想要的結果。 :) –

+0

還補充說明。 –

回答

4

按照評論:

如果什麼元素都不在隨之而來的訂單?

在這種情況下(假設XSLT 1.0),可以使用translate()來獲取元素的ID,然後搜索通過正確的名稱使用concat()相應的要素爲基礎。我會將following-sibling::軸更改爲../parent::的縮寫),以確保最終捕獲當前的firstname之前的元素。

<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="customers"> 
     <MyCustomers> 
      <xsl:apply-templates select="*[starts-with(name(),'firstname')]"/> 
     </MyCustomers> 
    </xsl:template> 

    <xsl:template match="*[starts-with(name(),'firstname')]"> 
     <xsl:variable name="id" select="translate(name(),'firstname','')"/> 

     <Customer> 
      <Name><xsl:value-of select="concat(.,' ', 
        ../*[name()=concat('lastname',$id)])"/></Name> 
      <Sex><xsl:value-of select="../*[name()=concat('sex',$id)]"/></Sex> 
     </Customer> 
    </xsl:template> 

</xsl:stylesheet> 

作廢答案

假設如圖問題固定輸入文檔結構,一個優良的工作XSLT 1.0變換是:

<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="customers"> 
     <MyCustomers> 
      <xsl:apply-templates select="*[starts-with(name(),'firstname')]"/> 
     </MyCustomers> 
    </xsl:template> 

    <xsl:template match="*[starts-with(name(),'firstname')]"> 
     <Customer> 
      <Name><xsl:value-of select="concat(.,' ', 
        following-sibling::*[1] 
        [starts-with(name(),'lastname')])"/></Name> 
      <Sex><xsl:value-of select="following-sibling::*[2] 
        [starts-with(name(),'sex')]"/></Sex> 
     </Customer> 
    </xsl:template> 

</xsl:stylesheet> 

小解釋

由於XML輸入中標記的悲傷名稱,您需要XPath 1.0函數starts-with()。您可以使用following-sibling::軸獲取名稱以firstname開頭的任何元素的所需以下兄弟標籤。

+0

感謝您的回答。但是,我們如果元件不隨之而來的訂單: \t 肖恩 \t 弗蘭克 \t 殺手 \t 中號 \t 伍茲 \t 詹妮弗 \t \t ˚F \t 中號 sean

+0

如果元素,隨之而來的訂單都沒有,你的樣品輸入不恰當地反映你的真實XML。但是,如果元素不是按順序排列的,那麼你怎麼知道某個'sex'標籤屬於某個'firstname'呢?你能更清楚嗎? –

+0

我現在看到了,你的意思是依附於元素名稱中的整數? –

0

下面是一個XSLT 2.0樣式表,它將得到您要查找的輸出,即使它們不是按順序排列的。它也按「名字」元素名稱排序。

示例XML輸入(混合起來以顯示不同的順序):

<customers> 
    <lastname1>Killer</lastname1> 
    <sex3>F</sex3> 
    <firstname2>Frank</firstname2> 
    <firstname1>Sean</firstname1> 
    <lastname2>Woods</lastname2> 
    <sex2>M</sex2> 
    <firstname3>Jennifer</firstname3> 
    <sex1>M</sex1> 
    <lastname3>Lee</lastname3> 
</customers> 

XSLT樣式表2.0(同撒克遜-HE 9進行測試。3):

<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 
    <xsl:output indent="yes"/> 
    <xsl:strip-space elements="*"/> 

    <xsl:template match="node()|@*"> 
    <xsl:choose> 
     <xsl:when test="name()[starts-with(.,'firstname')]"> 
     <xsl:variable name="suffix" select="substring(name(),10)"></xsl:variable> 
     <xsl:message><xsl:value-of select="$suffix"/></xsl:message> 
     <customer> 
      <Name> 
      <xsl:value-of select="concat(.,' ',/customers/*[starts-with(name(),'lastname')][ends-with(name(),$suffix)])"/> 
      </Name> 
      <Sex> 
      <xsl:value-of select="/customers/*[starts-with(name(),'sex')][ends-with(name(),$suffix)]"/> 
      </Sex> 
     </customer> 
     </xsl:when> 
     <xsl:when test="name()='customers'"> 
     <MyCustomers> 
      <xsl:apply-templates> 
      <xsl:sort select="name()[starts-with(.,'firstname')]"></xsl:sort> 
      </xsl:apply-templates> 
     </MyCustomers> 
     </xsl:when> 
     <xsl:otherwise> 
     <xsl:apply-templates select="node()|@*"/> 
     </xsl:otherwise> 
    </xsl:choose> 
    </xsl:template> 

</xsl:stylesheet> 

輸出:

<MyCustomers> 
    <customer> 
     <Name>Sean Killer</Name> 
     <Sex>M</Sex> 
    </customer> 
    <customer> 
     <Name>Frank Woods</Name> 
     <Sex>M</Sex> 
    </customer> 
    <customer> 
     <Name>Jennifer Lee</Name> 
     <Sex>F</Sex> 
    </customer> 
</MyCustomers> 
0

這種轉變產生,即使頂部元素的子元素以任意方式洗牌想要的結果:

<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:variable name="vNumCustomers" 
     select="count(/*/*) div 3"/> 

<xsl:template match="/*"> 
    <MyCustomers> 
     <xsl:for-each select= 
      "*[not(position() > $vNumCustomers)]"> 
     <xsl:variable name="vNum" select="position()"/> 

     <Customer> 
      <Name> 
      <xsl:value-of select= 
      "concat(/*/*[name()=concat('firstname',$vNum)], 
        ' ', 
        /*/*[name()=concat('lastname',$vNum)] 
        ) 
      "/> 
      </Name> 
      <Sex> 
      <xsl:value-of select= 
      "/*/*[name()=concat('sex',$vNum)] 
      "/> 
      </Sex> 
     </Customer> 
     </xsl:for-each> 
    </MyCustomers> 
</xsl:template> 
</xsl:stylesheet> 

當應用於此XML文檔(任意所提供的一個的重新洗牌):

<customers> 
    <sex1>M</sex1> 
    <lastname2>Woods</lastname2> 
    <lastname1>Killer</lastname1> 
    <sex2>M</sex2> 
    <firstname3>Jennifer</firstname3> 
    <firstname2>Frank</firstname2> 
    <lastname3>Lee</lastname3> 
    <firstname1>Sean</firstname1> 
    <sex3>F</sex3> 
</customers> 

有用,正確的結果產生

<MyCustomers> 
    <Customer> 
     <Name>Sean Killer</Name> 
     <Sex>M</Sex> 
    </Customer> 
    <Customer> 
     <Name>Frank Woods</Name> 
     <Sex>M</Sex> 
    </Customer> 
    <Customer> 
     <Name>Jennifer Lee</Name> 
     <Sex>F</Sex> 
    </Customer> 
</MyCustomers> 

說明

  1. 我們計算提供數據的客戶數量。變量$vNumCustomers保存這些數據。

  2. 對於每個客戶{i}(i = 1到$vNumCustomers),我們創建相應的<Customer{i}>元素。爲了避免使用遞歸,我們在這裏使用the Piez method

+0

我喜歡「count(/ */*)div 3」的想法,但如果源xml包含其他不相關的元素,則這可能不起作用,如: 2011-07-11 ...... sean

+0

@sean:當然。有一個更通用的解決方案,我只需要找一些空閒時間來適應我的答案。我目前的答案是在5分鐘內完成的。 –

+0

我明白這一點。順便說一句,你喜歡哪個解決方案?這是爲每個循環解決方案還是模板匹配解決方案? – sean