2013-03-14 49 views
2

我想使用LINQ获取不同值的字典。 我一直在使用这种尝试:使用LINQ的不同字典

var roleRefList = 
    xDocument.Root.Descendants() 
      .Where(x => x.Name.LocalName.Equals("roleRef") && 
         !string.IsNullOrEmpty(Convert.ToString(x.Attributes().FirstOrDefault(a => a.Name.LocalName.Equals("roleURI")))) && 
         !string.IsNullOrEmpty(Convert.ToString(x.Attributes().FirstOrDefault(a => a.Name.LocalName.Equals("href"))))) 
      .Select(l => new { 
        roleUri = l.Attributes().FirstOrDefault(a => a.Name.LocalName.Equals("roleURI")).Value, 
        href = l.Attributes().FirstOrDefault(a => a.Name.LocalName.Equals("href")).Value 
       }) 
      .Distinct() 
      .ToDictionary(a => a.roleUri); 

的这里的问题是,当有在roleUri重复项,然后会出现错误。 我解析XML文档并创建xElement属性的字典roleUriroleref(如果它们存在于xElement中)。

其他的解决方法是使用一个for循环:

Dictionary<string, string> roleRefList = new Dictionary<string, string>(); 
      foreach (XElement element in xDocument.Root.Descendants().Where(x => x.Name.LocalName.Equals("roleRef"))) 
      { 
       string roelUri = Convert.ToString(element.Attributes().FirstOrDefault(a => a.Name.LocalName.Equals("roleURI"))); 
       string href = Convert.ToString(element.Attributes().FirstOrDefault(a => a.Name.LocalName.Equals("href"))); 
       if (!string.IsNullOrEmpty(roelUri) && !string.IsNullOrEmpty(href) && !roleRefList.ContainsKey(roelUri)) 
       { 
        roleRefList.Add(roelUri, href); 
       } 
      } 

,但我想实现这个使用LINQ。

+1

为什么要使用Linq实现此功能?如果您有工作代码? – Maarten 2013-03-14 06:53:42

+0

使用节点本地名称的原因是什么?你的XML有不同的名称空间声明?你能展示你正在解析的xml的例子吗? – 2013-03-14 07:07:23

+0

所以根据你的循环:如果'roleUri'不止一次存在,你只需要它的第一个实例(和它的'roleRef')在Dictionary中?我有这个权利吗? – Merenzo 2013-03-14 07:10:02

回答

3

你可以编写自己的Distinct方法,将Func<T,TKey>作为参数。你可以找到例子,在这里:Distinct list of objects based on an arbitrary key in LINQ

根据该方法,你应该能够编写:

var roleRefList = xDocument.Root.Descendants().Where(x => x.Name.LocalName.Equals("roleRef") && !string.IsNullOrEmpty(Convert.ToString(x.Attributes().FirstOrDefault(a => a.Name.LocalName.Equals("roleURI")))) && !string.IsNullOrEmpty(Convert.ToString(x.Attributes().FirstOrDefault(a => a.Name.LocalName.Equals("href"))))) 
          .Select(l => new 
          { 
           roleUri = l.Attributes().FirstOrDefault(a => a.Name.LocalName.Equals("roleURI")).Value, 
           href = l.Attributes().FirstOrDefault(a => a.Name.LocalName.Equals("href")).Value 
          }).Distinct(l => l.roleUri).ToDictionary(a => a.roleUri); 

更新

或者你可以用分组:

var roleRefList = xDocument.Root.Descendants().Where(x => x.Name.LocalName.Equals("roleRef") && !string.IsNullOrEmpty(Convert.ToString(x.Attributes().FirstOrDefault(a => a.Name.LocalName.Equals("roleURI")))) && !string.IsNullOrEmpty(Convert.ToString(x.Attributes().FirstOrDefault(a => a.Name.LocalName.Equals("href"))))) 
          .Select(l => new 
          { 
           roleUri = l.Attributes().FirstOrDefault(a => a.Name.LocalName.Equals("roleURI")).Value, 
           href = l.Attributes().FirstOrDefault(a => a.Name.LocalName.Equals("href")).Value 
          }) 
          .GroupBy(l => l.roleUri) 
          .ToDictionary(g => g.Key, g => g.FirstOrDefault()); 
+0

谢谢先生。其工作..再次感谢一吨 – 2013-03-14 07:34:21

+0

+1。可以使用g.First而不是g.FirstOrDefault吗?我希望这个小组总是至少有一个成员。 – Merenzo 2013-03-14 07:40:40

+0

是的,你也可以使用'First' – MarcinJuraszek 2013-03-14 07:42:39

0

几种实现方法:

1)您可以使用自定义相等比较器调用不同的方法。为了达到这个目的,你需要编写一个类,它首先包含roleUri和href字段。喜欢的东西:

public class AttributePair 
    { 
     public string RoleUri {get; set;} 
     public string Href {get; set;} 
    } 

下一步是写上您的课相等比较:

public class AttributePairComparer : IEqualityComparer<AttributePair> 
    { 
     public bool Equals(AttributePair x, AttributePair y) 
     { 
      return x.RoleUri.Equals(y.RoleUri); 
     } 

     public int GetHashCode(AttributePair obj) 
     { 
      return obj.RoleUri.GetHashCode(); 
     } 
    } 

而在这之后我们就通过相等比较器的实例的独特方法:

var roleRefList = xDocument.Root.Descendants().Where(x => x.Name.LocalName.Equals("roleRef") && !string.IsNullOrEmpty(Convert.ToString(x.Attributes().FirstOrDefault(a => a.Name.LocalName.Equals("roleURI")))) && !string.IsNullOrEmpty(Convert.ToString(x.Attributes().FirstOrDefault(a => a.Name.LocalName.Equals("href"))))) 
         .Select(l => new AttributePair 
         { 
          RoleUri = l.Attributes().FirstOrDefault(a => a.Name.LocalName.Equals("roleURI")).Value, 
          Href = l.Attributes().FirstOrDefault(a => a.Name.LocalName.Equals("href")).Value 
         }).Distinct(new AttributePairComparer()).ToDictionary(a => a.RoleUri, a => a.Href); 

我同意这个解决方案相当复杂,但这是实现所需结果的经典方式,可以这么说。

2)另一种解决方案是MoreLinqDistinctBy方法。其中可以传递lambda表达式作为参数:

var roleRefList = xDocument.Root.Descendants().Where(x => x.Name.LocalName.Equals("roleRef") && !string.IsNullOrEmpty(Convert.ToString(x.Attributes().FirstOrDefault(a => a.Name.LocalName.Equals("roleURI")))) && !string.IsNullOrEmpty(Convert.ToString(x.Attributes().FirstOrDefault(a => a.Name.LocalName.Equals("href"))))) 
         .Select(l => new 
         { 
          roleUri = l.Attributes().FirstOrDefault(a => a.Name.LocalName.Equals("roleURI")).Value, 
          href = l.Attributes().FirstOrDefault(a => a.Name.LocalName.Equals("href")).Value 
         }).DistinctBy(p => p.roleUri).ToDictionary(a => a.roleUri, a => a.href); 

3) C#的总设计师,安德斯·海尔斯伯格,建议其使用的GroupBy该溶液中。你可以阅读关于它here.

1

重复的属性是不允许在XML中。如果你将有roleRef元素2个roleURI属性,那么你将的XDocument装载过程中得到的异常:

“roleURI”是重复的属性名称。第42行,第42位。

所以,实际上你的代码应该是这样的:

var xdoc = XDocument.Load("foo.xml"); 
XNamespace ns = "http://www.adventure-works.com"; // put your namespace here 

Dictionary<string, string> roleRefList = 
    xdoc.Root.Descendants(ns + "roleRef") 
     .Select(r => new { 
      Uri = (string)r.Attribute("roleURI"), 
      Href = (string)r.Attribute("href") 
     }) 
     .Where(r => !String.IsNullOrEmpty(r.Uri) && !String.IsNullOrEmpty(r.Href)) 
     .ToDictionary(r => r.Uri, r => r.Href); 

结果是一样与你为循环。示例xml:

<root xmlns="http://www.adventure-works.com"> 
    <roleRef/> 
    <roleRef roleURI=""/> 
    <roleRef href=""/> 
    <roleRef roleURI="" href=""/> 
    <roleRef roleURI="a" /> 
    <roleRef roleURI="" href="b"/> 
    <roleRef roleURI="c" href="d"/> 
</root> 
+0

它给我错误当我给一个名称空间值,但它与name.localname – 2013-03-14 07:42:11

+0

@lokendrajayaswal如果你没有命名空间,然后离开原样(或删除ns码)。如果你定义了名称空间(例如''),那么创建'XNamespace ns =“http://www.adventure-works.com”;'。看看linq这里的xml命名空间http://msdn.microsoft.com/en-us/library/system.xml.linq.xnamespace.aspx – 2013-03-14 08:30:55