2011-05-09 44 views
6

我有XML这看起来是这样的 -如何使用XSLT输出重复的元素?

<Root> 
    <Fields> 
    <Field name="abc" displayName="aaa" /> 
    <Field name="pqr" displayName="ppp" /> 
    <Field name="abc" displayName="aaa" /> 
    <Field name="xyz" displayName="zzz" /> 
    </Fields> 
</Root> 

我所要的输出仅含有具有重复的name-displayName组合这些元素,如果有任何 -

<Root> 
     <Fields> 
     <Field name="abc" displayName="aaa" /> 
     <Field name="abc" displayName="aaa" /> 
     </Fields> 
</Root> 

我该怎么办这使用XSLT?

+0

好问题,+1。请参阅我的答案,了解简单,高效的XSLT 1.0解决方案。 – 2011-05-09 13:19:03

+0

还添加了XSLT 2.0解决方案。 – 2011-05-09 13:30:29

+0

我的回答对你有用吗? – 2011-05-10 12:37:52

回答

7

此转换

<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="kFieldByName" match="Field" 
    use="concat(@name, '+', @displayName)"/> 

<xsl:template match= 
    "Field[generate-id() 
     = 
     generate-id(key('kFieldByName', 
        concat(@name, '+', @displayName) 
        )[2]) 
     ] 
    "> 
    <xsl:copy-of select= 
    "key('kFieldByName',concat(@name, '+', @displayName))"/> 
</xsl:template> 
</xsl:stylesheet> 

当所提供的XML文档施加:

<Root> 
    <Fields> 
     <Field name="abc" displayName="aaa" /> 
     <Field name="pqr" displayName="ppp" /> 
     <Field name="abc" displayName="aaa" /> 
     <Field name="xyz" displayName="zzz" /> 
    </Fields> 
</Root> 

产生想要的结果

<Field name="abc" displayName="aaa"/> 
<Field name="abc" displayName="aaa"/> 

说明

  1. Muenchian grouping使用组合键(在namedisplayName属性上)。

  2. 代码中唯一的模板匹配其对应组中第二个的任何Field元素。然后,在模板的主体内部,输出整个组。

  3. Muenchian分组是在XSLT 1.0中进行分组的有效方法。密钥用于提高效率。

  4. 另请参阅我的回答this question

二, 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:template match="/"> 
    <xsl:for-each-group select="/*/*/Field" 
      group-by="concat(@name, '+', @displayName)"> 
     <xsl:sequence select="current-group()[current-group()[2]]"/> 
    </xsl:for-each-group> 
</xsl:template> 
</xsl:stylesheet> 

当这个变换所提供的XML文档(以上示出)施加,再次有用,正确的结果产生

<Field name="abc" displayName="aaa"/> 
<Field name="abc" displayName="aaa"/> 

说明

  1. 使用<xsl:for-each-group>

  2. 使用current-group()功能。

1

找到重复的,你需要遍历的Field元素和每一个,找一组在整个文档中Field元素具有匹配namedisplayName属性值。如果该集合有多个元素,则将该元素添加到输出中。

下面是实现这一点的模板的一个例子:

<xsl:template match="Field"> 
    <xsl:variable name="fieldName" select="@name" /> 
    <xsl:variable name="fieldDisplayName" select="@displayName" /> 
    <xsl:if test="count(//Field[@name=$fieldName and @displayName=$fieldDisplayName]) > 1"> 
     <xsl:copy-of select="."/> 
    </xsl:if> 
</xsl:template> 

执行在您的样本数据的模板(包装在一个适当的XSLT文件)给出以下输出:

<?xml version="1.0" encoding="utf-8"?> 
<Root> 
    <Fields> 
    <Field name="abc" displayName="aaa" /> 
    <Field name="abc" displayName="aaa" /> 
    </Fields> 
</Root> 
+0

@Jeff Yates:这是一个可能的解决方案,但是它的效率是O(N^2),并且它对于包含大量Field元素的XML文档使用速度太慢。查看我的答案以获得有效的解决方案 – 2011-05-09 13:21:29

+0

@Dimitre:似乎愚蠢地做了比必要的更多的努力。没有理由相信真正的XML会很大,并且没有分析信息。我会尽快编写完成,直到分析结束。 – 2011-05-09 13:28:07

+0

@Jeff Yates:人们可以也应该使用已知的最有效的解决方案。因为人们认为不然,我们每天都会遇到40分钟的转换问题,并且在重构Muenchian分组时只需要2秒。我们不应该传播糟糕和幼稚的算法。 – 2011-05-09 13:33:29