19

I have an XML result like this

<response>
  <lst name="responseHeader">
    <int name="status">0</int>
    <int name="QTime">16</int>
  </lst>
  <result name="response" numFound="3" start="0" maxScore="1.0">
    <doc>
      <str name="ContaFirstname">
        firstname1                                                   
      </str>
      <str name="ContaId">6557</str>
      <str name="ContaJobTitle">Manager</str>
      <str name="ContaSurname">surname1
      </str>
    </doc>
    <doc>
      <str name="ContaFirstname">firstname2</str>
      <str name="ContaId">6203</str>
      <str name="ContaJobTitle">Director</str>
      <str name="ContaSurname">surname2</str>
    </doc>
  </result>
</response>

I want to get a list of objects, and every object will contain the value of ContaFirstname, ContaId, ContaJobTitle and ContaSurname

I tried something like this, but that's not right because I get them all NULL

var test = from c in xml.Descendants("doc")
                    select new 
                    {
                        firstname = c.Element("ContaFirstname"),
                        surnmane = c.Element("ContaSurname")
                    }; 

So how can access these elements by name?

kbaccouche
  • 4,575
  • 11
  • 43
  • 65

2 Answers2

32

You don't want to access the elements by name as most people would interpret that statement. You want to access the elements by the value of their name attribute:

firstname = (string) c.Elements("str")
                      .First(x => x.Attribute("name").Value == "ContaFirstname");
//etc

You may well want to abstract that into a separate method, as it's going to be a pain to do it multiple times. For example:

public static XElement ElementByNameAttribute(this XContainer container,
                                              string name)
{
    return container.Elements("str")
                    .First(x => x.Attribute("name").Value == name);
}

Then:

var test = from c in xml.Descendants("doc")
           select new 
           { 
               firstname = c.ElementByNameAttribute("ContaFirstname").Value,
               surnmane = c.ElementByNameAttribute("ContaSurname").Value
           }; 

If you have any chance to give your document a more sensible structure, that would be preferable...

Alex K.
  • 171,639
  • 30
  • 264
  • 288
Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • Thanks for your answer and you are right that was confusing. I edited my question's title to avoid that. – kbaccouche Jan 24 '13 at 15:25
  • @the_ruby_racer: See my edit to include an extension method which will make your life easier than repeating the same code time and time again. – Jon Skeet Jan 24 '13 at 15:44
  • 4
    You forgot a `>` in `First(x => x.Attribute("name").Value == name);` ? – Ouadie Oct 06 '13 at 14:21
  • Additionally it makes sense to add the extension to `XContainer` instead of `XElement` so other objects inheriting of `XContainer` can also use it (e.g. `XDocument`) – Marwie Oct 12 '15 at 15:50
  • @Marwie: Yup, makes sense to me. Done. – Jon Skeet Oct 12 '15 at 23:33
3

Does this solve your problem:

var test = from c in xml.Descendants("doc")
           select new 
           {
               firstname = c.Elements("str").First(element => element.Attribute("name").Value == "ContaFirstname"),
               surnmane = c.Elements("str").First(element => element.Attribute("name").Value == "ContaSurname")
           }; 

or, if you want the values (instead of XElement:

var test = from c in xml.Descendants("doc")
           select new 
           {
               firstname = c.Elements("str").First(element => element.Attribute("name").Value == "ContaFirstname").Value,
               surnmane = c.Elements("str").First(element => element.Attribute("name").Value == "ContaSurname").Value
           }; 
Alex Nolasco
  • 18,750
  • 9
  • 86
  • 81
Cédric Bignon
  • 12,892
  • 3
  • 39
  • 51