使用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>
|
|
转载于:https://www.cnblogs.com/sunsonbaby/archive/2004/09/10/41948.html