2013-03-14 31 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