我正在尝试使用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属性roleUri
和roleref
的字典,如果它们存在于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实现这个。
答案 0 :(得分:4)
您可以编写自己的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());
答案 1 :(得分:1)
XML中的重复属性不允许。如果roleURI
元素中有两个roleRef
属性,那么在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);
结果与 for 循环相同。示例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>
答案 2 :(得分:0)
实现这一目标的几种方法:
1)您可以使用自定义相等比较器调用distinct方法。为了实现这一点,您需要编写一个首先包含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();
}
}
之后我们将刚才比较器的实例传递给distinct方法:
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)另一种解决方案是来自DistinctBy的MoreLinq方法。您可以将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#的首席架构师Anders Hejlsberg建议使用GroupBy的解决方案。你可以阅读它here.