[转]SharePoint内容定制之XSLT高级用法——使用Muenchian方法分组XML数据

分组在XSLT处理中是一个很常见的问题,怎样才能将一系列XML节点元素组织到不同的分组里?最常见的情况是处理从数据库中得到的XML输出,数据库通常是根据数据表中的记录生成结果。例如,一个地址簿可能输出下面的内容:

<records>

<contact id="0001">

<title>Mr</title>

<forename>John</forename>

<surname>Smith</surname>

</contact>

<contact id="0002">

<title>Dr</title>

<forename>Amy</forename>

<surname>Jones</surname>

</contact>

...

</records>

现在的问题是怎样把这段文本转换成多个按照surname来分组的列表,例如:

Jones,<br />

Amy (Dr)
<br />

Brian (Mr)
<br />

Smith,
<br />

Fiona (Ms)
<br />

John (Mr)
<br />

通过以下两个步骤可实现这个需求:

  1. 找出都有哪些surname。
  2. 获取具有相同surname值的所有contact节点。

要找出都有哪些surname就要先找出每个surname对应的第一个contact节点,一个方法就是找出那些surname与先前节点的surname都不同的的contact节点。

contact[not(surname = preceding-sibling::contact/surname)]

一旦这些contact节点被找出,就很容易找出它们对应的surname了,然后在此基础上再找出具有相同surname的所有contact节点。

<xsl:apply-templates select="/records/contact[surname = current()/surname]"/>

但是对于那些比较大的XML数据(比如海量数据库),这种方法也存在缺点,它使用了两个XPath查询进行了大量的处理。使用preceding-siblings遍历所有前驱兄弟节点会花费大量的时间,尤其是当前节点处于尾部的时候。同样地,查找所有具有某个指定surname的contact节点也会遍历所有contact节点。这种做法效率是很低的。

Muenchian是由Steve Muench开发的一种分组方法,它通过keys更高效的实现这种需求。Keys通过分配一个key值给一个节点,然后就可以简单的通过key值来访问节点。如果存在多个节点有相同的key值,那么通过这个key值就可以取到所有这些节点。这意味着,如果你想根据节点的某个属性对一组节点进行高效分组时,可以使用keys。

再回到上面的例子,我们想通过surname来对所有的contact节点分组,于是就创建一个key,并且给每个contact节点一个key值(也就是surname值)。需要分组的节点匹配match属性指定的表达式,key值通过use属性的表达式指定,如下:

<xsl:key name="contacts-by-surname" match="contact" use="surname"/>

在key定义好以后,如果知道了surname,我们便可快速查找到相应的contact节点,例如下面的表达式将给出所有surname为Smith的contact节点。

key('contacts-by-surname', 'Smith')

然后这就可以很容易实现上面提到的第二个步骤。

<xsl:apply-templates select="key('contacts-by-surname', surname)"/>

不过,我们首先需要做的是找出都有哪些surname,同时找出具有某个surname的第一个contact节点。在这里我们可以再次使用keys。当我们把surname作为key的时候,contact节点就已经是这个集合的一部分了,但它是否是集合中的第一个元素呢(按照XML文档顺序)?还是在排在后面?我们仅仅关心集合中的第一个元素。

要找出一个contact节点是否为由key筛选出的结果中的第一个元素,就要把contact和由key筛选出的结果中的第一个元素作比较。有很多常用的方法可以用来判断两个节点是不是同一个节点:

1. 比较通过generate-id()函数生成的唯一标识符。

contact[generate-id() = generate-id(key('contacts-by-surname', surname)[1])]

2. 检查由这两个节点组成的新集合内部有一个还是两个节点——因为同一个节点在节点集合中不可能出现两次,所以如果这个集合中只有一个元素,那么这之前提到的两个节点肯定就是同一个节点。

contact[count(. | key('contacts-by-surname', surname)[1]) = 1]

在找出所有的组以后,可以按照需求对它们排序。同样,组内的节点也可以进行排序。最终,利用下面的这个template,我们就可以分组输出从数据中取到的XML信息。

<xsl:key name="contacts-by-surname" match="contact" use="surname"/>

<xsl:template match="records">

<xsl:for-each select="contact[count(. | key('contacts-by-surname', surname)[1]) = 1]">

<xsl:sort select="surname"/>

<xsl:value-of select="surname"/>,<br />

<xsl:for-each select="key('contacts-by-surname', surname)">

<xsl:sort select="forename"/>

<xsl:value-of select="forename"/> (<xsl:value-of select="title"/>)<br />

</xsl:for-each>

</xsl:for-each>

</xsl:template>

通常,Muenchian是最好的节点分组方法,因为它不需要遍历大量的节点,所以更高效。这种方法特别适用于处理从数据库中获取到的表结构数据,例如以某种层级关系重新组织这些从数据库中得到的数据。它也适用于任何情况下利用XPath可访问到的节点属性对节点集合进行分组。

Muenchian有一个缺点,它只能在支持keys的XSLT处理器下使用。这就排除了James Clark的xt还有2000年5月以前发布的MSXML。另外,使用keys也是相当消耗内存资源的,因为所有的节点和key值都必须保存在内存中。最后,在多个XML文档之间使用keys进行分组也是一项比较复杂的工作。

相关链接:

XSLT Recommendation: Keys

XSLT Recommendation: generate-id()

[原文地址:http://www.jenitennison.com/xslt/grouping/muenchian.html]

原文地址:https://www.cnblogs.com/kxlf/p/2230966.html