Reputation: 123
how can I query an xml file where I have multiple items with the same name, so that I can get all items back. Currently I only get the first result back. I managed to get it to work with the following code, but this returns all items where the specific search criteria is met. What I want as output is to get two results back where the location is Dublin for example. The question is how can I achieve this with linq to xml
Cheers Chris,
Here is the code
string location = "Oslo";
var training = (from item in doc.Descendants("item")
where item.Value.Contains(location)
select new
{
event = item.Element("event").Value,
event_location = item.Element("location").Value
}).ToList();
The xml file looks like this
<training>
<item>
<event>C# Training</event>
<location>Prague</location>
<location>Oslo</location>
<location>Amsterdam</location>
<location>Athens</location>
<location>Dublin</location>
<location>Helsinki</location>
</item>
<item>
<event>LINQ Training</event>
<location>Bucharest</location>
<location>Oslo</location>
<location>Amsterdam</location>
<location>Helsinki</location>
<location>Brussels</location>
<location>Dublin</location>
</item>
</training>
Upvotes: 4
Views: 2833
Reputation: 1500514
You're using item.Element("location")
which returns the first location element under the item. That's not necessarily the location you were looking for!
I suspect you actually want something more like:
string location = "Oslo";
var training = from loc in doc.Descendants("location")
where loc.Value == location
select new
{
event = loc.Parent.Element("event").Value,
event_location = loc.Value
};
But then again, what value does event_location
then provide, given that it's always going to be the location you've passed into the query?
If this isn't what you want, please give more details - your question is slightly hard to understand at the moment. Details of what your current code gives and what you want it to give would be helpful - as well as what you mean by "name" (in that it looks like you actually mean "value").
EDIT: Okay, so it sounds like you want:
string location = "Oslo";
var training = from loc in doc.Descendants("location")
where loc.Value == location
select new
{
event = loc.Parent.Element("event").Value,
event_locations = loc.Parent.Elements("location")
.Select(e => e.Value)
};
event_locations will now be a sequence of strings. You can get the output you want with:
for (var entry in training)
{
Console.WriteLine("Event: {0}; Locations: {1}",
entry.event,
string.Join(", ", entry.event_locations.ToArray());
}
Give that a try and see if it's what you want...
Upvotes: 5
Reputation: 30418
This might not be the most efficient way of doing it, but this query works:
var training = (from item in root.Descendants("item")
where item.Value.Contains(location)
select new
{
name = item.Element("event").Value,
location = (from node in item.Descendants("location")
where node.Value.Equals(location)
select node.Value).FirstOrDefault(),
}).ToList();
(Note that the code wouldn't compile if the property name was event
, so I changed it to name
.)
I believe the problem with your code was that the location
node retrieved when creating the anonymous type didn't search for the node with the desired value.
Upvotes: 0