Linq to xml parsing

I need assistance in retrieving the element Country and Its value 1 in the below code. Thanks in advance.

<?xml version="1.0" encoding="utf-8"?> 
<env:Contentname xmlns:env="http://data.schemas" xmlns="http://2013-02-01/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <env:Body>
       <env:Content action="Hello">
         <env:Data xsi:type="Yellow">
         </env:Data>
       </env:Content>

      <env:Content action="Hello">
         <env:Data xsi:type="Red">
           <Status xmlns="http://2010-10-10/">
             <Id >39681</Id>
             <Name>Published</Name>
           </Status>
        </env:Data>
      </env:Content>

      <env:Content action="Hello">
        <env:Data xsi:type="green">
          <Document>
            <Country>1</Country>   
          </Document>
        </env:Data>
      </env:Content>
  </env:Body>
</env:Contentname>

I tried this,

          var result = from x in root.Descendants(aw + "Data")

          where (string)x.Attribute(kw + "type") == "green"

          select x;


        foreach (var item in result)
        {
            var str = item.Element("Document").Element("Country");
            Console.WriteLine(str.Value);
        }

But i am getting error.(Object reference not set to an instance of an object.) kindly help me with this.

Jon Skeet
people
quotationmark

This is the problem, for two reasons:

var str = item.Element("Document").Element("country");

Firstly, XML is case-sensitive - you want Country, not country.

Secondly, those elements inherit the namespace declared with xmlns=... in the root element. You need:

XNamespace ns = "http://2013-02-01/";
...
var element = item.Element(ns + "Document").Element(ns + "Country");

I'd also encourage you to avoid query expressions where they don't actually buy you much. In this case, you could perform the whole query in one go using the Elements extension method which works on sequences of input elements, assuming you don't mind finding every Document -> Country element rather than just one per Data:

var query = root.Descendants(aw + "Data")
                .Where(x => (string)x.Attribute(kw + "type") == "green")
                .Elements(ns + "Document")
                .Elements(ns + "Country")
                .Select(x => x.Value);
foreach (var item in query)
{
    Console.WriteLine(item);
}

One significant difference - this won't fall over with an exception if there is a Data element with xsi:type='green' which doesn't have a Document -> Country element. If you want it to (to find bad data) you could use:

var query = root.Descendants(aw + "Data")
                .Where(x => (string)x.Attribute(kw + "type") == "green")
                .Select(x => x.Element(ns + "Document")
                              .Element(ns + "Country")
                              .Value);

To show a short but complete example, this code (with your XML as test.xml) gives an output of "1":

using System;
using System.Linq;
using System.Xml.Linq;

public class Program
{
    static void Main(string[] args)
    {
        var doc = XDocument.Load("test.xml");
        XNamespace ns = "http://2013-02-01/";
        XNamespace kw = "http://www.w3.org/2001/XMLSchema-instance";
        XNamespace aw = "http://data.schemas";
        var query = doc.Descendants(aw + "Data")
                .Where(x => (string)x.Attribute(kw + "type") == "green")
                .Elements(ns + "Document")
                .Elements(ns + "Country")
                .Select(x => x.Value);
        foreach (var item in query)
        {
            Console.WriteLine(item);
        }
    }
}

people

See more on this question at Stackoverflow