該變換使用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
。
此外,對於任何字符串$s
,substring($s, Infinity)
的值只是空字符串。當然,對於任何字符串$s
,substring($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>
問得好,+1。現在您將能夠使用複合鍵學習和應用Muenchian分組。 –
@_Jared:您需要解釋(通過編輯問題來更好)用國家代碼加前綴的規則:僅限Isit 2位或三位數字,還是不同的位數?如果是後者,那麼解決方案應該提供所有可能的國家代碼的列表。 –
@_Dimitre - 道歉,希望我現在更清楚了。在我遇到這個障礙之前,我非常接近自己的工作。非常感謝您的幫助! – Jared