我有以下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>
我想获得这样的输出(例如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>
e.g。 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
属性的sms元素是“(未知)”,但@address
在两个元素之间是唯一的,即它们不应该组合在一起,因为sms消息来自不同的数字/ person(即使联系人姓名相同,也无关紧要)。
我是否应该尝试重新排序/更改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个字符,通常是最后一个字符根据网络而变化)
数字(@address
)按{+ 1}}保存为+ 64-21-12345(不包括短划线)或021-12345(不包括破折号)。
答案 0 :(得分:10)
此转换使用具有复合键的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中一次通过这样做非常棘手.Anultipass解决方案更容易,但一般来说需要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文档(前一个+添加了三个sms
元素contact_name="Jared"
,其中两个具有“相同”的地址,新发布的规则):
<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>
详细解释:
这个问题的主要困难来自于XPath 1.0中没有“if ... then ... else”运算符,但是我们必须指定一个XPath表达式。 use
指令的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
本身。
<强> II。 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>