关于c#:根据来自Linq查询的属性值过滤掉重复的XElements

Filtering out duplicate XElements based on an attribute value from a Linq query

我正在使用 Linq 尝试过滤掉任何与 "name" 属性具有相同值的重复 XElement。

原始xml:

1
2
3
4
5
6
7
8
<foo>
<property name="John" value="Doe" id="1" />
<property name="Paul" value="Lee" id="1" />
<property name="Ken" value="Flow" id="1" />
<property name="Jane" value="Horace" id="1" />
<property name="Paul" value="Lee" id="1" />
... other xml properties with different id's
</foo>
1
2
3
4
5
6
7
8
9
10
// project elements in group into a new XElement
// (this is for another part of the code)
var props = group.data.Select( f => new XElement("property",
    new XAttribute("name", f.Attribute("name").Value), f.Attribute("value"));

// filter out duplicates
props = props.Where(f => f.ElementsBeforeSelf()
                          .Where(g => g.Attribute("name").Value ==
                                      f.Attribute("name").Value)
                          .Count() == 0);

很遗憾,过滤步骤不起作用。我认为 Where() 过滤器会检查当前元素之前具有相同属性名称的任何元素,然后将其包含在大于零的集合中,从而排除当前元素(称为 \\'f\\'),但那没有发生。想法?


您可以创建一个 IEqualityComparer 与 Distinct() 一起使用,这应该可以满足您的需求。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class Program
{
    static void Main(string[] args)
    {
        string xml ="<foo><property name="John" value="Doe" id="1"/><property name="Paul" value="Lee" id="1"/><property name="Ken" value="Flow" id="1"/><property name="Jane" value="Horace" id="1"/><property name="Paul" value="Lee" id="1"/></foo>";

        XElement x = XElement.Parse(xml);
        var a = x.Elements().Distinct(new MyComparer()).ToList();
    }
}

class MyComparer : IEqualityComparer<XElement>
{
    public bool Equals(XElement x, XElement y)
    {
        return x.Attribute("name").Value == y.Attribute("name").Value;
    }

    public int GetHashCode(XElement obj)
    {
        return obj.Attribute("name").Value.GetHashCode();
    }
}


我认为您应该先删除重复项,然后再进行投影。例如:

1
2
3
4
5
6
var uniqueProps = from property in doc.Root
                  group property by (string)property.Attribute("name") into g
                  select g.First() into f
                  select new XElement("property",
                      new XAttribute("name", f.Attribute("name").Value),
                      f.Attribute("value"));

或者,如果您更喜欢方法语法,

1
2
3
4
5
6
var uniqueProps = doc.Root
    .GroupBy(property => (string)property.Attribute("name"))
    .Select(g => g.First())
    .Select(f => new XElement("property",
                     new XAttribute("name", f.Attribute("name").Value),
                     f.Attribute("value")));


您的方法有点奇怪,例如,您不需要将元素投影到新元素中;当您将现有元素添加到新文档时,它才有效(tm)。

我将简单地按 name 属性对 <property> 元素进行分组,然后从每个组中选择第一个元素:

1
2
3
4
5
6
var doc = XDocument.Parse(@"<foo>...</foo>");

var result = new XDocument(new XElement("foo",
    from property in doc.Root
    group property by (string)property.Attribute("name") into g
    select g.First()));