tags:

views:

317

answers:

3

Hi All

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>
+3  A: 

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...

Jon Skeet
Hi Jon, thanks for the reply, I tried your code, but it gives the same result. I posted a more detailed description of what I would like to do. Cheers Chris
Chris
Hi Jon, thanks again for your help, what I've seemed to miss was the Parent.Element part. Now it's working...
Chris
A: 

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.

Andy
A: 

Hi Jon, Andy,

thanks for the reply. What I'm trying to do, is to query an event sheet based on the location. The result should be a list of events that match the searched city.

For example: Location Search String "Oslo"

Event: C# Training Location: Oslo, Prague, Amsterdam, etc.

:::::::::::::::::

Event: Linq Training Location: Bucharest, Oslo, Amsterdam, Helsinki, etc.

:::::::::::::::::::

Event: C# Advanced Training Location: Munich, Kopenhagen, Rome, Oslo, etc.

The results I currently get when searching for the city, is the first item only (c# code: ... where item.Element("title").Value == location ... ) In this case the "C# Training", or depending on the code all Items where the location string is in (c# code: where item.Value.Contains(location) ). What I want to get back is two events.

Hope this makes it a little bit clearer where I'm stuck at.

cheers, Chris

Chris
I've edited my answer to hopefully solve the problem.
Jon Skeet