2011-09-14 126 views
7

我有以下XML組由XML多個屬性與XSLT

<smses> 
    <sms address="87654321" type="1" body="Some text" readable_date="3/09/2011 2:16:52 PM" contact_name="Person1" /> 
    <sms address="87654321" type="2" body="Some text" readable_date="3/09/2011 2:36:41 PM" contact_name="Person1" /> 
    <sms address="87654321" type="1" body="Some text" readable_date="3/09/2011 2:16:52 PM" contact_name="Person1" /> 
    <sms address="123" type="2" body="Some text" readable_date="3/09/2011 10:56:24 AM" contact_name="Person2" /> 
    <sms address="123" type="1" body="Some text" readable_date="3/09/2011 10:57:52 AM" contact_name="Person2" /> 
    <sms address="123" type="2" body="Some text" readable_date="3/09/2011 10:56:24 AM" contact_name="Person2" /> 
    <sms address="12345678" type="1" body="Some text" readable_date="3/09/2011 11:21:16 AM" contact_name="Person3" /> 
    <sms address="12345678" type="2" body="Some text" readable_date="3/09/2011 11:37:21 AM" contact_name="Person3" /> 

    <sms address="12345" type="2" body="Some text" readable_date="28/01/2011 7:24:50 PM" contact_name="(Unknown)" /> 
    <sms address="233" type="1" body="Some text" readable_date="30/12/2010 1:13:41 PM" contact_name="(Unknown)" /> 
</smses> 

我試圖讓這樣的(如XML)的輸出中

<sms contact_name="person1"> 
    <message type="1">{@body}</message> 
    <message type="2">{@body}</message> 
    <message type="1">{@body}</message> 
</sms> 
<sms contact_name="person2"> 
    <message type="2">{@body}</message> 
    <message type="1">{@body}</message> 
</sms> 
<sms contact_name="person3"> 
    <message type="2">{@body}</message> 
    <message type="1">{@body}</message> 
</sms> 
<sms contact_name="(Unknown)"> 
    <message type="2">{@body}</message> 
    <message type="1">{@body}</message> 
</sms> 
<sms contact_name="(Unknown)"> 
    <message type="2">{@body}</message> 
</sms> 

例如HTML

<div> 
    <h1>Person: @contact_name (@address)</h1> 
    <p>message @type: @body</p> 
</div> 

我已經成功地做到這一點與下面的XSLT代碼(請原諒下面的代碼並不完全反映HTML,輸出是期望的結果!)

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 
    <xsl:output method="xml" indent="yes" /> 
    <xsl:key name="txt" match="sms" use="@contact_name" /> 
    <xsl:template match="smses"> 
     <xsl:apply-templates select="sms[generate-id(.)=generate-id(key('txt', @contact_name)[1])]"> 
      <xsl:sort select="@address" order="ascending" /> 
     </xsl:apply-templates> 
    </xsl:template> 
    <xsl:template match="sms"> 
     <h4><xsl:value-of select="@contact_name" /></h4> 
      <xsl:for-each select="key('txt', @contact_name)"> 
        <br /> 
        <xsl:value-of select="@body" /> 
      </xsl:for-each> 
    </xsl:template> 

</xsl:stylesheet> 

這個問題我有,或者是我問的問題。我有一個短信元素,其@contact_name屬性爲「(未知)」,但@address在兩個元素之間是唯一的,即它們不應該組合在一起,因爲短信息來自不同的號碼/人(即使聯繫人姓名是一樣的,它是無關的)。 我應該嘗試重新排序/更改XML數據還是有辦法讓XSLT識別未知組應該檢查@address@contact_name是否相同。

編輯:

我忘了(或者說忘了),雖然有與相同@contact_name和獨特的@address一些短信還存在一些@address領域的有一點出入,他們不案例國家代碼在數字前面,例如

<sms contact_name="jared" address="12345" /> 
<sms contact_name="jared" address="+64112345" /> 

但他們是爲了進行分組,因爲他們都來自同一人/數

編輯:

在我的情況則僅可能有具有3字符的差異(例如,64)的國家代碼加上2位網絡代碼(例如21)。基本上,結果應該是,如果@contact_name =相同並@address是完全不同的 即

<sms contact_name="jared" address="12345" /> 
<sms contact_name="jared" address="5433467" /> 

那麼就應該單獨的元件,因爲它們是從不同的人/數(一個或多個)。

如果@contact_name =相同並@address是不同僅由國家和網絡代碼 即

<sms contact_name="jared" address="02112345" /> 
<sms contact_name="jared" address="+642112345" /> 

然後,因爲它們是來自同一人,他們應該被分組/數

編輯:

國家代碼:+64(3個字符)

網絡代碼:021(3個字符,usua LLY最後一個字符視網絡情況)

號(@address)得到保存每<sms>顯示爲+ 64-21-12345(不含破折號)或021-12345(不包括破折號)變化。

+0

問得好,+1。現在您將能夠使用複合鍵學習和應用Muenchian分組。 –

+0

@_Jared:您需要解釋(通過編輯問題來更好)用國家代碼加前綴的規則:僅限Isit 2位或三位數字,還是不同的位數?如果是後者,那麼解決方案應該提供所有可能的國家代碼的列表。 –

+0

@_Dimitre - 道歉,希望我現在更清楚了。在我遇到這個障礙之前,我非常接近自己的工作。非常感謝您的幫助! – Jared

回答

9

該變換使用Muenchian分組的複合密鑰

<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:key name="kContactByNameAddress" match="sms" 
      use="concat(@contact_name,'+',@address)"/> 

<xsl:template match= 
    "sms[generate-id() 
     = 
     generate-id(key('kContactByNameAddress', 
         concat(@contact_name,'+',@address) 
         ) 
         [1] 
        ) 
     ] 
    "> 
    <sms contact_name="{@contact_name}"> 
     <xsl:apply-templates mode="inGroup" 
     select="key('kContactByNameAddress', 
       concat(@contact_name,'+',@address) 
       )"/> 
    </sms> 
</xsl:template> 

<xsl:template match="sms" mode="inGroup"> 
     <message type="{@type}"> 
     <xsl:value-of select="@body"/> 
     </message> 
</xsl:template> 
<xsl:template match="sms"/> 
</xsl:stylesheet> 

當施加到所提供的XML文檔

<smses> 
    <sms address="87654321" type="1" body="Some text" 
    readable_date="3/09/2011 2:16:52 PM" contact_name="Person1" /> 
    <sms address="87654321" type="2" body="Some text" 
    readable_date="3/09/2011 2:36:41 PM" contact_name="Person1" /> 
    <sms address="87654321" type="1" body="Some text" 
    readable_date="3/09/2011 2:16:52 PM" contact_name="Person1" /> 
    <sms address="123" type="2" body="Some text" 
    readable_date="3/09/2011 10:56:24 AM" contact_name="Person2" /> 
    <sms address="123" type="1" body="Some text" 
    readable_date="3/09/2011 10:57:52 AM" contact_name="Person2" /> 
    <sms address="123" type="2" body="Some text" 
    readable_date="3/09/2011 10:56:24 AM" contact_name="Person2" /> 
    <sms address="12345678" type="1" body="Some text" 
    readable_date="3/09/2011 11:21:16 AM" contact_name="Person3" /> 
    <sms address="12345678" type="2" body="Some text" 
    readable_date="3/09/2011 11:37:21 AM" contact_name="Person3" /> 
    <sms address="12345" type="2" body="Some text" 
    readable_date="28/01/2011 7:24:50 PM" contact_name="(Unknown)" /> 
    <sms address="233" type="1" body="Some text" 
    readable_date="30/12/2010 1:13:41 PM" contact_name="(Unknown)" /> 
</smses> 

有用,正確的結果產生

<sms contact_name="Person1"> 
    <message type="1">Some text</message> 
    <message type="2">Some text</message> 
    <message type="1">Some text</message> 
</sms> 
<sms contact_name="Person2"> 
    <message type="2">Some text</message> 
    <message type="1">Some text</message> 
    <message type="2">Some text</message> 
</sms> 
<sms contact_name="Person3"> 
    <message type="1">Some text</message> 
    <message type="2">Some text</message> 
</sms> 
<sms contact_name="(Unknown)"> 
    <message type="2">Some text</message> 
</sms> 
<sms contact_name="(Unknown)"> 
    <message type="1">Some text</message> 
</sms> 

更新:OP編輯了他的問題併發布了新的要求,即address屬性可能或不可能以國家代碼開頭。如果國家代碼之後的子字符串與另一個地址相同,則兩個地址中的一個帶有contry代碼,另一個不帶國家代碼的地址「相同」。在這種情況下,這兩個元素應該組合在一起。

下面是解(這將是微不足道的在XSLT 2.0寫,但在XSLT 1.0在單次通過這樣做是相當棘手。Amultipass解決方案更容易,但它通常將需要xxx:node-set()擴展功能,因此會失去便攜性):

<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:key name="kContactByNameAddress" match="sms" 
    use="concat(@contact_name,'+', 
       concat(substring(@address, 
           4 div starts-with(@address,'+')), 
        substring(@address, 
           1 div not(starts-with(@address,'+')) 
          ) 
        ) 
      )"/> 

<xsl:template match= 
    "sms[generate-id() 
     = 
     generate-id(key('kContactByNameAddress', 
         concat(@contact_name,'+', 
           concat(substring(@address, 
               4 div starts-with(@address,'+')), 
             substring(@address, 
               1 div not(starts-with(@address,'+')) 
               ) 
             ) 
           ) 
         ) 
         [1] 
        ) 
     ] 
    "> 
    <sms contact_name="{@contact_name}"> 
     <xsl:apply-templates mode="inGroup" 
     select="key('kContactByNameAddress', 
       concat(@contact_name,'+', 
         concat(substring(@address, 
             4 div starts-with(@address,'+')), 
           substring(@address, 
             1 div not(starts-with(@address,'+')) 
             ) 
           ) 
         ) 
       ) 
     "/> 
    </sms> 
</xsl:template> 

<xsl:template match="sms" mode="inGroup"> 
     <message type="{@type}"> 
     <xsl:value-of select="@body"/> 
     </message> 
</xsl:template> 
<xsl:template match="sms"/> 
</xsl:stylesheet> 

當施加於下面的XML文檔(前一個這種轉化+加入contact_name="Jared" 3個sms元件,其中的兩個具有「相同」的地址,根據新發布的規則):

<smses> 
    <sms address="87654321" type="1" body="Some text" 
     readable_date="3/09/2011 2:16:52 PM" contact_name="Person1" /> 
    <sms address="87654321" type="2" body="Some text" 
     readable_date="3/09/2011 2:36:41 PM" contact_name="Person1" /> 
    <sms address="87654321" type="1" body="Some text" 
     readable_date="3/09/2011 2:16:52 PM" contact_name="Person1" /> 
    <sms address="123" type="2" body="Some text" 
     readable_date="3/09/2011 10:56:24 AM" contact_name="Person2" /> 
    <sms address="123" type="1" body="Some text" 
     readable_date="3/09/2011 10:57:52 AM" contact_name="Person2" /> 
    <sms address="123" type="2" body="Some text" 
     readable_date="3/09/2011 10:56:24 AM" contact_name="Person2" /> 
    <sms address="12345678" type="1" body="Some text" 
     readable_date="3/09/2011 11:21:16 AM" contact_name="Person3" /> 
    <sms contact_name="jared" address="12345" type="2" body="Some text"/> 
    <sms contact_name="jared" address="56789" type="1" body="Some text"/> 
    <sms contact_name="jared" address="+6412345" type="2" body="Some text"/> 
    <sms address="12345678" type="2" body="Some text" 
     readable_date="3/09/2011 11:37:21 AM" contact_name="Person3" /> 
    <sms address="12345" type="2" body="Some text" 
     readable_date="28/01/2011 7:24:50 PM" contact_name="(Unknown)" /> 
    <sms address="233" type="1" body="Some text" 
     readable_date="30/12/2010 1:13:41 PM" contact_name="(Unknown)" /> 
</smses> 

想要的,正確的結果產生

<sms contact_name="Person1"> 
    <message type="1">Some text</message> 
    <message type="2">Some text</message> 
    <message type="1">Some text</message> 
</sms> 
<sms contact_name="Person2"> 
    <message type="2">Some text</message> 
    <message type="1">Some text</message> 
    <message type="2">Some text</message> 
</sms> 
<sms contact_name="Person3"> 
    <message type="1">Some text</message> 
    <message type="2">Some text</message> 
</sms> 
<sms contact_name="jared"> 
    <message type="2">Some text</message> 
    <message type="2">Some text</message> 
</sms> 
<sms contact_name="jared"> 
    <message type="1">Some text</message> 
</sms> 
<sms contact_name="(Unknown)"> 
    <message type="2">Some text</message> 
</sms> 
<sms contact_name="(Unknown)"> 
    <message type="1">Some text</message> 
</sms> 

詳細解釋

在這個問題的主要困難來自於事實,沒有「如果... then ... else「操作符在XPath 1.0中,但是我們必須在use屬性中指定單個XPath表達式在xsl:key指令中選擇address屬性(當它不以「+」開頭)或其國家代碼後的子字符串(如果其字符串值以「+」開頭)時選擇該屬性。

這裏評估時,我使用這個可憐的男人的實施

if($condition) 
    then $string1 
    else $string2 

以下XPath表達式,相當於上述:從事實

concat(substring($string1, 1 div $condition), 
     substring($string2, 1 div not($condition)) 
    ) 

這等價如下那1 div true()是與1 div 1相同,這是1,而1 div false()是與1 div 0相同,那就是數字(正數)Infinity

此外,對於任何字符串$ssubstring($s, Infinity)的值只是空字符串。當然,對於任何字符串$ssubstring($s, 1)的值只是字符串$s本身。

二, XSLT 2.0解決方案

<xsl:stylesheet version="2.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="/*"> 
    <xsl:for-each-group select="sms" group-by= 
    "concat(@contact_name,'+', 
      if(starts-with(@address,'+')) 
      then substring(@address, 4) 
      else @address 
      )"> 
    <sms contact_name="{@contact_name}"> 
     <xsl:apply-templates select="current-group()"/> 
    </sms> 

    </xsl:for-each-group> 
</xsl:template> 

<xsl:template match="sms"> 
     <message type="{@type}"> 
     <xsl:value-of select="@body"/> 
     </message> 
</xsl:template> 
</xsl:stylesheet> 

當這個(!簡單得多)XSLT 2.0變換在同一個XML文檔(以上)應用,同樣正確的輸出產生

<sms contact_name="Person1"> 
    <message type="1">Some text</message> 
    <message type="2">Some text</message> 
    <message type="1">Some text</message> 
</sms> 
<sms contact_name="Person2"> 
    <message type="2">Some text</message> 
    <message type="1">Some text</message> 
    <message type="2">Some text</message> 
</sms> 
<sms contact_name="Person3"> 
    <message type="1">Some text</message> 
    <message type="2">Some text</message> 
</sms> 
<sms contact_name="jared"> 
    <message type="2">Some text</message> 
    <message type="2">Some text</message> 
</sms> 
<sms contact_name="jared"> 
    <message type="1">Some text</message> 
</sms> 
<sms contact_name="(Unknown)"> 
    <message type="2">Some text</message> 
</sms> 
<sms contact_name="(Unknown)"> 
    <message type="1">Some text</message> 
</sms> 
+0

你很有知識!非常感謝您的回答。唉,我想我已經失去了我的例子,最爲此道歉!我編輯了我的問題來更新它。 – Jared

+0

非常棒的夥計,以及關於它在做什麼的史詩般的解釋。非常感謝,我想我可以從這裏拿走:) – Jared

+0

@Jared:不客氣。如果問題中有其他信息,例如,如果知道所有電話號碼(不含國家代碼)具有相同的固定長度,則解決方案可以更簡單。如果您的情況如此,請確認,我很樂意發佈更簡單的解決方案。 –