linq to xml enumerating over descendants
Hi trying to write a simple linq query from a tutorial I read. But i cannot seem to get it to work. I am trying to display both the address in the attached xml document, but can only display the first one. Can someone help me figure out why both aren't being printed. Thank you very much
<?xml version="1.0" encoding="utf-8" ?>
<Emails>
<Email group="FooBar">
<Subject>Test subject</Subject>
<Content>Test Content</Content>
<EmailTo>
<Address>foo@bar.com</Address>
<Address>bar@foo.com</Address>
</EmailTo>
</Email>
</Emails>
Dim steve = (From email In emailList.Descendants("Email") _
Where (email.Attribute("group").Value.Equals("FooBar")) _
Select content = email.Element("EmailTo").Descendants("Address"))开发者_如何学编程.ToList()
If Not steve Is Nothing Then
For Each addr In steve
Console.WriteLine(addr.Value)
Next
Console.ReadLine()
End If
Your current query returns a List<IEnumerable<XElement>>
. That means you need two nested foreach
loops: one to loop over the list, and another to loop over the content of the IEnumerable<XElement>
.
Instead, you could update your LINQ query to use the Enumerable.SelectMany
method and get to the addresses directly. In query format a SelectMany
is represented by using a second from
clause to indicate a subquery. This would resemble the following:
Dim query = (From email In emailList.Descendants("Email") _
Where (email.Attribute("group").Value.Equals("FooBar")) _
From addr In email.Element("EmailTo").Descendants("Address") _
Select addr.Value).ToList()
If query.Any() Then
For Each addr In query
Console.WriteLine(addr)
Next
End If
Also, the ToList
isn't needed if you only want to iterate over the results and don't intend to use the result as a list for other purposes.
EDIT: to explain how this query works let's break it down in 2 parts:
First:
From email In emailList.Descendants("Email") _
Where (email.Attribute("group").Value.Equals("FooBar")) _
This queries for all <Email>
nodes and only matches the ones that have a group attribute value of "FooBar".
Second:
From addr In email.Element("EmailTo").Descendants("Address") _
Select addr.Value
This is a subquery that continues where the first part (above) ended. It essentially is a way to further query the results of the original query. Here we query for all <Address>
nodes and, finally, select their Value
for the inner text of the nodes. The reason we need to do this is because Descendants("Address")
returns a IEnumerable<XElement>
containing all "Address" elements. We need to perform an additional query (or foreach
) to iterate over those values and extract their values.
Another way to illustrate this is by breaking it down in 2 queries:
Dim query1 = From email In emailList.Descendants("Email") _
Where (email.Attribute("group").Value.Equals("FooBar"))
Select email.Element("EmailTo").Descendants("Address")
Dim query2 = query1.SelectMany(Function(addr) addr.Select(Function(a) a.Value))
Notice the use of SelectMany
in query2
. The Select
in query2
is that additional effort to loop over the IEnumerable
that I mentioned earlier. The original query is clearer than query1/query2, but I wrote them just to clarify the point.
精彩评论