SherCoder
SherCoder

Reputation: 688

Linq to XML: parse some elements and skip some elements

I am trying to parse data from XDocument instance using Linq to XML and store it in IEnumerable<MyClass>. I do not want to get every element from the xml document, so I am using ElementAt(#index) to get the value. However sometimes the element that I want is not in the document and then I get index out of range exception. Is there a better way to parse values from this document? Here is the code:

XML Document looks like this:

<someotherelement>   /* I dont want the "someotherelement" part either */
   <subelement></subelement>
   <subelement></subelement>
</someotherelement>
<result>
   <doc>
      <int name="personid">8394</int> /* I don't want this value so I am skipping it */
      <str name="name">James</str>
      <str name="address">30 Awesome Lane</str>
      <arr name="randomearray"> /* I want to skip this too */
         <str name="random1">SomeValue</str>
         <str name="random2">SomeValue</str>
         <str name="random2">SomeValue</str>
      </arr>
      <str name="city">Awesome City</str>
      <str name="state">CA</str>
      <int name="zipcode">84392</int>
      <str name="country">USA</str>
      <str name="phonenumber">8309933820</str>
      <date name="reportdate">2012-07-27T06:01:05.256Z</date>
   </doc>

   /* This is missing address and country */
   <doc>
      <int name="personid">10394</int> /* I don't want this value so I am skipping it */
      <str name="name">Mathew</str>
      <arr name="randomearray"> /* I want to skip this too */
         <str name="random1">SomeValue</str>
         <str name="random2">SomeValue</str>
         <str name="random2">SomeValue</str>
      </arr>
      <str name="city">Not Awesome City</str>
      <str name="state">CA</str>
      <str name="zipcode">58439</str>
      <str name="phonenumber">8309933820</str>
      <date name="reportdate">2012-07-27T06:01:05.256Z</date>
   </doc>
</result>    

Class:

public class Doc
{
    public string Name {get;set;}
    public string Address{get;set;}
    public string City {get;set;}
    public string State {get;set;}
    public int ZipCode {get;set;}
    public string Country {get;set;}        
    public string PhoneNumber {get;set;}
    public DateTime ReportDate {get;set;}
}

Linq To Xml

string pathToXml = "http://www.foo.com/something";
var doc = XDocument.Load(pathToXml);
IEnumerable<Doc> myDoc = from item in doc.Descendants("doc")
                 select new Doc
                 {
                   Name = item.Element("str").Value,
                   Address = item.Element("str").ElementsAfterSelf("str").First().Value,
                   City = item.Element("str").ElementsAfterSelf("str").ElementAt(1).Value,
                   State = item.Element("str").ElementsAfterSelf("str").ElementAt(2).Value,
                   ZipCode = Convert.ToInt32(item.Element("int").Value)
                   Country = item.Element("str").ElementsAfterSelf("str").ElementAt(3).Value,
                   PhoneNumber = item.Element("str").ElementsAfterSelf("str").ElementAt(4).Value,
                   ReportDate = Convert.ToDateTime(item.Element("date").Value)
                 };

Upvotes: 0

Views: 1796

Answers (2)

twoflower
twoflower

Reputation: 6830

You may want to employ XPath which allows you to specify the elements more comfortably, using for example the values of their attributes, which seems useful in your case.

Combined with an extension method to avoid repetitive statements:

public static string GetElementValue(this XElement element, string query)
{
    var elem = element.XPathSelectElement(query);
    return elem == null ? null : elem.Value;
}

An example:

select new Doc
{
    Name = item.GetElementValue("str[@name='name']"),
    ...
}

Upvotes: 2

sblom
sblom

Reputation: 27343

I fixed this by adding an extension method:

public static class Extensions {
  public static string GetValue(this XElement element) {
    return element == null ? null : element.Value;
  }
}

And then using Attribute()-based selection of your values:

IEnumerable<Doc> myDoc = from item in doc.Descendants("doc")
                 select new Doc
                 {
                   Name = item.Element("str").Value,
                   Address = item.Element("str").ElementsAfterSelf("str").First().Value,
                   City = item.Element("str").ElementsAfterSelf("str").Where(el => (string)el.Attribute("name") == "city").FirstOrDefault().GetValue(),
                   State = item.Element("str").ElementsAfterSelf("str").Where(el => (string)el.Attribute("name") == "state").FirstOrDefault().GetValue(),
                   ZipCode = Convert.ToInt32(item.Element("int").Value),
                   Country = item.Element("str").ElementsAfterSelf("str").Where(el => (string)el.Attribute("name") == "country").FirstOrDefault().GetValue(),
                   PhoneNumber = item.Element("str").ElementsAfterSelf("str").Where(el => (string)el.Attribute("name") == "phonenumber").FirstOrDefault().GetValue(),
                   ReportDate = Convert.ToDateTime(item.Element("date").Value)
                 };

There may be a built-in for that extension method, but not one that I know of.

Upvotes: 1

Related Questions