我有以下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>
Run Code Online (Sandbox Code Playgroud)
我想获得这样的输出(例如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>
Run Code Online (Sandbox Code Playgroud)
例如html
<div>
<h1>Person: @contact_name (@address)</h1>
<p>message @type: @body</p>
</div>
Run Code Online (Sandbox Code Playgroud)
我已经设法使用以下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>
Run Code Online (Sandbox Code Playgroud)
我遇到的问题是,或者更确切地说是我要问的问题.我有一个带有@contact_name"(未知)"属性的短信元素,但@address两个元素之间是唯一的,即它们不应该组合在一起,因为短信息来自不同的数字/人(即使联系人姓名是同样,它无关紧要).我是否应该尝试重新排序/更改XML数据,或者是否有办法让XSLT识别未知的组,如果@address相同则检查是否不同@contact_name.
编辑:
我没有提及(或者更确切地说忘记)虽然有一些具有相同@contact_name和唯一的短信息,@address但也存在一些@address字段在数字前没有国家代码的情况下存在轻微差异的情况,例如
<sms contact_name="jared" address="12345" />
<sms contact_name="jared" address="+64112345" />
Run Code Online (Sandbox Code Playgroud)
但他们是为了进行分组,因为他们都来自同一人/数字.
编辑:
在我的情况下,只有3个字符(例如+64)国家代码加2位数网络代码(例如21)的差异.基本上结果应该是,如果@contact_name=相同且@address完全不同,即
<sms contact_name="jared" address="12345" />
<sms contact_name="jared" address="5433467" />
Run Code Online (Sandbox Code Playgroud)
然后它们应该是单独的元素,因为它们来自不同的人/数字.
if @contact_name=相同且@address仅因国家和网络代码而异
<sms contact_name="jared" address="02112345" />
<sms contact_name="jared" address="+642112345" />
Run Code Online (Sandbox Code Playgroud)
那么他们应该按照同一个人/数字进行分组
编辑:
国家代码:+64(3个字符)
网络代码:021(3个字符,通常最后一个字符根据网络而变化)
Numbers(@address)每个保存<sms>为+ 64-21-12345(不包括破折号)或021-12345(不包括破折号).
Dim*_*hev 11
此转换使用具有复合键的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>
Run Code Online (Sandbox Code Playgroud)
应用于提供的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>
Run Code Online (Sandbox Code Playgroud)
产生了想要的正确结果:
<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>
Run Code Online (Sandbox Code Playgroud)
更新:OP已编辑了他的问题,并发布了该address属性可能或可能不以国家/地区代码开头的新要求.如果国家代码后的子字符串等于另一个地址,则两个地址,一个具有contry代码,另一个没有国家代码,"相同".在这种情况下,两个元素应该组合在一起.
这是解决方案(在XSLT 2.0中写入是微不足道的,但在XSLT 1.0中,在单次传递中这样做是非常棘手的.多路径解决方案更容易,但它通常需要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>
Run Code Online (Sandbox Code Playgroud)
当此转换应用于以下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>
Run Code Online (Sandbox Code Playgroud)
产生了想要的正确结果:
<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>
Run Code Online (Sandbox Code Playgroud)
详细说明:
这个问题的主要困难来自于XPath 1.0中没有"if ... then ... else"运算符,但是我们必须use在xsl:key指令的属性中指定一个XPath表达式,要么选择address属性(当它不以"+"开头)或其国家代码后面的子串(如果其字符串值以"+"开头).
在这里,我正在使用这个穷人的实施
if($condition)
then $string1
else $string2
Run Code Online (Sandbox Code Playgroud)
以下XPath表达式在计算时等效于上述:
concat(substring($string1, 1 div $condition),
substring($string2, 1 div not($condition))
)
Run Code Online (Sandbox Code Playgroud)
从这一事实,这等价如下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>
Run Code Online (Sandbox Code Playgroud)
当这个(更简单!)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>
Run Code Online (Sandbox Code Playgroud)