In a nutshell, you have to pay attention to what namespaces are defined. My problem was I forgot to consider the default namespace. These two topics on MSDN explain it in more detail: Working with XML Namespaces and Scope of Default Namespaces in C#.
To illustrate the problem precisely, suppose you have the following XML:
1 2 3 4 5 6 7 | < contacts > < contact > < name >name</ name > </ contact > </ contacts > |
Then with this C# code you can get the name of the contact.
1 2 3 4 5 6 7 8 | XDocument xdoc = XDocument.Load(MapPath( "~/App_Data/XMLFile.xml" )); var nameQuery1 = xdoc.Descendants( "contact" ); foreach (XElement nameElement in nameQuery1) { Response.Write(nameElement.Element( "name" ).Value); } |
Now suppose the XML is defined with a default namespace such as this:
1 2 3 4 5 6 7 |
In this case Code Listing 1 would not return the name element because it doesn’t take in account the default namespace. The following code will work:
1 2 3 4 5 6 7 8 | var nameQuery2 = xdoc.Descendants(ns + "contact" ); foreach (XElement nameElement in nameQuery2) { Response.Write(nameElement.Element(ns + "name" ).Value); } |
Now, suppose there is an additional namespace declared with a prefix defined such as this:
1 2 3 4 5 6 7 8 | < contact > < name >name</ name > < an:note >note</ an:note > </ contact > </ contacts > |
Possible C# code to get the note element is:
1 2 3 4 5 6 7 8 | var nameQuery3 = xdoc.Descendants(ns + "contact" ); foreach (XElement nameElement in nameQuery3) { Response.Write(nameElement.Element(ns2 + "note" ).Value); } |
The code examples above are just for illustration purposes. You should make the code more robust, e.g. using try/catch clauses, for your particular application.
The following XML is an excerpt from the Messenger Connect AllContacts ATOM feed. It is followed by code to read it. It runs in the context of a web page, but the code can be easily taken out and used in other scenarios.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 | <? xml version = "1.0" encoding = "utf-8" ?> < feed xml:base = "http://bay.apis.live.net/V4.1/cid-3333333333333333/" xmlns:xml = "http://www.w3.org/XML/1998/namespace" xmlns = "http://www.w3.org/2005/Atom" > < title type = "text" >AllContacts</ title > < id >uuid:1623d873-0cc4-45ac-bea0-8c09fad60370;id=9850</ id > < updated >2010-09-13T20:57:55Z</ updated > < link rel = "self" type = "application/atom+xml;type=feed" title = "self" href = "Contacts/AllContacts" /> < link rel = "edit" type = "application/atom+xml;type=feed" title = "edit" href = "Contacts/AllContacts" /> < entry p3:reserved = " " p4:etag = "0001-01-01T00:00:00.0000000" xmlns:p4 = "http://schemas.microsoft.com/ado/2007/08/dataservices/metadata" xmlns:p3 = "http://schemas.microsoft.com/ado/2007/08/dataservices" > < id >urn:uuid:F2E3AWHYXZYUVHICHHNFXIXKHQ</ id > < title type = "text" >Cosentino Luparello</ title > < updated >2010-08-05T19:11:17Z</ updated > < link rel = "self" type = "application/atom+xml;type=entry" title = "self" href = "Contacts/AllContacts/F2E3AWHYXZYUVHICHHNFXIXKHQ" /> < link rel = "edit" type = "application/atom+xml;type=entry" title = "edit" href = "Contacts/AllContacts/F2E3AWHYXZYUVHICHHNFXIXKHQ" /> < category term = "Contact" label = "Contact" scheme = "http://schemas.microsoft.com/ado/2007/08/dataservices/scheme" /> < content type = "application/xml" > < p3:Emails > < p3:element > < p3:Type >None</ p3:Type > </ p3:element > </ p3:Emails > < p3:FirstName >Cosentino</ p3:FirstName > < p3:FormattedName >Cosentino Luparello</ p3:FormattedName > < p3:LastName >Luparello</ p3:LastName > < p3:Locations > < p3:element > < p3:City >Palermo</ p3:City > < p3:Type >None</ p3:Type > </ p3:element > </ p3:Locations > < p3:PhoneNumbers /> < p3:Urls /> </ p4:properties > </ content > </ entry > </ feed > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 | <%@ Page Language= "C#" AutoEventWireup= "true" %> <%@ Import Namespace= "System" %> <%@ Import Namespace= "System.Collections.Generic" %> <%@ Import Namespace= "System.Linq" %> <%@ Import Namespace= "System.Web" %> <%@ Import Namespace= "System.Web.UI" %> <%@ Import Namespace= "System.Xml.Linq" %> <script runat= "server" > public class ContactName { public string FirstName { get; set; } public string LastName { get; set; } } protected void Page_Load(object sender, EventArgs e) { // Atom namespace. // Dataservices namespace, p3. // Dataservices metadata namespace, p4. XDocument xdoc = XDocument.Load(MapPath( "~/App_Data/data.xml" )); Output( "Query 1: Selecting content node and then iterating on it to find FirstName and LastName." ); // Select the content nodes directly. var contentQuery = xdoc.Descendants(ns + "content" ); // Iterate over the collection of content nodes. foreach (XElement contentElement in contentQuery) { string firstName = contentElement.Descendants(nsp3 + "FirstName" ).FirstOrDefault().Value; string lastName = contentElement.Descendants(nsp3 + "LastName" ).FirstOrDefault().Value; Output(firstName + " " + lastName); } Output(); Output( "Query 2: Selecting FirstName and LastName as string." ); var firstNameQuery = xdoc.Descendants(nsp3 + "FirstName" ).Select(entry => entry.Value + " " + entry.ElementsAfterSelf(nsp3 + "LastName" ).First().Value); foreach (string firstNameEntry in firstNameQuery) { Output(firstNameEntry); } Output(); Output( "Query 3: Selecting FirstName and LastName as ContactName object." ); var contactNameQuery = xdoc.Descendants(nsp4 + "properties" ).Select(entry => new ContactName { FirstName = entry.Element(nsp3 + "FirstName" ).Value, LastName = entry.Element(nsp3 + "LastName" ).Value }); foreach ( var contactNameEntry in contactNameQuery) { Output(contactNameEntry.FirstName + " " + contactNameEntry.LastName); } Output(); Output( "Query 4: Query 3 + filtering results." ); var contactNameFilteredQuery = xdoc.Descendants(nsp4 + "properties" ) .Where(entry => entry.Element(nsp3 + "FirstName" ).Value.ToLower().StartsWith( "c" )) .Select(entry => new ContactName { FirstName = entry.Element(nsp3 + "FirstName" ).Value, LastName = entry.Element(nsp3 + "LastName" ).Value }); foreach ( var contactNameFilteredEntry in contactNameFilteredQuery) { Output(contactNameFilteredEntry.FirstName + " " + contactNameFilteredEntry.LastName); } } protected void Output() { Output( "" ); } protected void Output(string txt) { Label1.Text += txt + " " ; } </script> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd" > <head id= "Head1" runat= "server" > <title>Reading Windows Live Contact Atom Feed</title> </head> <body> <form id= "form1" runat= "server" > <div> Using LINQ extension methods and lambda expressions. <asp:Label ID= "Label1" runat= 'server' ></asp:Label> </div> </form> </body> </html> |
I've been Googling and trying to understand how parsing with a namespace works for over 2 hours but failed, thanks for this great article, I've totally understood how it works now.
ReplyDelete