您的位置:首页 > 其它

使用XSLT删除XML文件中的重复元素

2004-09-10 20:15 330 查看
使用XSLT删除XML文件中的重复元素
lookbook 翻译 (参与分:276,专家分:850) 发表:2003-4-16 下午9:42 更新:2003-4-17 上午9:49 版本:1.1 阅读:2458
[pre] 当进行XML数据转换的时候,我们经常会碰到XML数据文件中含有重复的元素。在这封技
术邮件中,我们将讨论一种解决该问题的方法。

问题:
让我们先来看一下具体的问题描述。假使有如下的一个XML数据文件,它包含了如下的内容:[/pre]
<Order>
<Item number="1">
<SKU>12345</SKU>
<Description>Standard Widget</Description>
</Item>
<Item number="2">
<SKU>54321</SKU>
<Description>Turbo Widget</Description>
</Item>
<Item number="3">
<SKU>12345</SKU>
<Description>Standard Widget</Description>
</Item>
</Order>

[pre] 在上面的XML数据文件中,每个Item元素都是单独显示的,请注意Item number1和Item number3
中的数据是一样的。但是,需求目标要求输出的XML数据文件中每个SKU元素个体不能重复,
并且要加上一个新的<Quantity>元素以显示每个Item元素数据的个数。需求目标的输出文件如下:[/pre]
<Order>
<Item>
<Quantity>2</Quantity>
<SKU>12345</SKU>
<Description>Standard Widget</Description>
</Item>
<Item>
<Quantity>1</Quantity>
<SKU>54321</SKU>
<Description>Turbo Widget</Description>
</Item>
</Order>

解决方法:
[pre] 提出的问题实际上有两个问题需要解决。第一,需要去除重复的SKU#12345元素个体(entry);
第二,需要提供一个新的<Quantity>元素以显示每个Item元素数据的个数。为了解决这些问
题,我们得使用一些XSLT的高级特性。
为了解决第一个问题,我们将使用XSLT的following操作。following和preceding操作
分别指示在一个for-each循环中的以后和以前节点(node)。following操作判断以后节点
如果和当前节点一样,则去除当前重复的节点。
为了解决第二个问题,我们需要得到每个Item元素的个数。幸运的是,XSLT提供计数(count)
功能。使用计数功能,我们可以对XML数据文件中出现的每个Item元素进行计数,并将这个
数值赋值给新建的<Quantity>元素。[/pre]

去除重复的元素个体:
[pre] 去除重复的元素个体需要一些小技巧。首先,将选择的节点放入一个for-each循环中,
但是这个循环中的select属性值需要一些小技巧。通常的做法,你会将所有的Item放入for-each
循环中,如下:[/pre]
<xsl:for-each select="//Order/Item">
. . .
</xsl:for-each>

[pre] 但是,我们需要每个SKU元素数据都唯一。为了能达到这种转换,我们得在select的属
性值中加入额外的信息。这个额外的信息将会告诉转换处理器只对以后节点和当前节点不同
的当前节点取值。举个小例子,如果第一个节点是A,下一个节点是A,那么就忽略第一个节
点;如果第一个节点是A,下一个节点是A,再下一个节点是B,那么循环之后对第二个节点
取值,第一个节点会被忽略。下面是该方法在XML的表达式:[/pre]
<xsl:for-each select="//SKU[not(.=following::SKU)]">
. . .
</xsl:for-each>

[pre] 在上面的XML表达式中,select的属性值决定了怎样循环取值选择的节点数据。它使得
我们只对以后节点和当前节点(用.表示)不同的当前节点取值。[/pre]

计数:
[pre] 需要对每个SKU元素进行计数并把它赋值给新建的<Quantity>元素,同样需要一些小技
巧。我们可以使用XSLT的计数(count)功能,但难题是告诉转换处理器需要对那些元素计数。
一个对所有SKU元素计数的简单例子如下:[/pre]
<xsl:value-of select="count(//SKU)"/>

[pre] 上面的XML表达式中,只是简单的对所有符合//SKU模式的元素进行计数。但是,我们需
要的是对满足特殊条件的SKU元素计数。技巧之处在于满足特殊条件的SKU元素值在每个for-each
循环中可以得到,并且是用点号(.)标识。那么解决计数问题的关键就是计数(count)功
能也需要使用点号(.)标识。所以,我们可以在每个for-each循环中,使用一个新变量,
如下所示:[/pre]
<xsl:variable name="thesku" select="."/>

[pre] 接着,我们就可以使用计数(count)功能来对每个SKU元素进行计数了,如下所示:[/pre]
<Quantity><xsl:value-of select="count(//SKU[.=$thesku])"/></Quantity>

完整的解决方法:
[pre] 现在,我们可以把以上所述的所有内容合并起来,得到该问题的完整解决方法。下面的
完整代码使用select属性来对SKU进行唯一性选择;而<Quantity>元素是使用计数XSLT的(count)
功能得到的;最后,<Description>元素的值是从原XML数据文件取值而来。[/pre]
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<Order>
<xsl:for-each select="//SKU[not(.=following::SKU)]">
<xsl:variable name="thesku" select="."/>
<Item>
<Quantity><xsl:value-of select="count(//SKU[.=$thesku])"/></Quantity>
<SKU><xsl:value-of select="." /></SKU>
<Description><xsl:value-of select="../Description"/></Description>
</Item>
</xsl:for-each>
</Order>
</xsl:template>
</xsl:stylesheet>

[pre]编者注释:由于技术错误,在先前的XML技术邮件"Tokenizing strings with Xalan-Java"
(March 20, 2002)中,里面的代码有一个错误,特此更正,完整正确的代码如下:[/pre]
To use the tokenize function, we'll create a template that calls it, like the following:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xalan="http://xml.apache.org/xalan">
<xsl:template match="/">
<xsl:for-each select="//CustomerAddress">
<Address><xsl:value-of select="Address1"/></Address>
<City><xsl:value-of select="xalan:tokenize(Address2, ' ,')[1]"/></City>
<State><xsl:value-of select="xalan:tokenize(Address2, ' ,')[2]"/></State>
<Zip><xsl:value-of select="xalan:tokenize(Address2, ' ,')[3]"/></Zip>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: