I receive the following string back from a service as a list
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<feed version="0.3" xmlns="http://purl.org/atom/ns#">
<entry>
<id>1</id>
<title>Job 1</title>
<author>
<name>Jim James</name>
</author>
<modified>2018-08-10T14:50:46-04:00</modified>
</entry>
<entry>
<id>2</id>
<title>Job 2</title>
<author>
<name>John Smith</name>
</author>
<modified>2018-09-10T14:50:46-04:00</modified>
</entry>
<entry>
<id>3</id>
<title>Job 3</title>
<author>
<name>Paul Rain</name>
</author>
<modified>2018-06-10T14:50:46-04:00</modified>
</entry>
<entry>
<id>4</id>
<title>Job 4</title>
<author>
<name>Jim James</name>
</author>
<modified>2018-08-10T14:50:46-04:00</modified>
</entry>
</feed>
I am attempting to grab the value of name through inner text but unable to do so:
i.e
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.LoadXml(response); //assuming response is above xml string
XmlNodeList parentNode = xmlDoc.GetElementsByTagName("entry");
foreach (XmlNode childrenNode2 in parentNode)
{
var b = childrenNode2.SelectSingleNode("name").InnerText ?? string.Empty;
}
Instead I'm getting object is null exception.
"ExceptionMessage": "Object reference not set to an instance of an object.",
Any pointers appreciated.
You have a few problems here:
You are using the old XmlDocument
API which has somewhat inconvenient handling of namespaces. Specifically:
XmlNode.GetElementsByTagName(string)
is namespace-unaware. It matches on the "raw" qualified element name, possibly including the prefix.
Of this method, Microsoft writes in documentation
Note
It is recommended that you use the
XmlNode.SelectNodes
orXmlNode.SelectSingleNode
method instead of theGetElementsByTagName
method.
XmlNode.SelectSingleNode(string)
, conversely, is namespace-aware and selects only elements in the empty namespace.
From the docs:
If the XPath expression does not include a prefix, it is assumed that the namespace URI is the empty namespace. If your XML includes a default namespace, you must still use the
XmlNamespaceManager
and add a prefix and namespace URI to it; otherwise, you will not get a selected node.
This inconsistency between the two methods explains why your code partly works, since all the elements in your XML document belong to the http://purl.org/atom/ns#
namespace.
If you indent your XML, it becomes apparent that the <name>
node is nested within a container <author>
node:
<feed version="0.3" xmlns="http://purl.org/atom/ns#">
<entry>
<id>1</id>
<title>Job 1</title>
<author>
<name>Jim James</name>
</author>
<modified>2018-08-10T14:50:46-04:00</modified>
</entry>
<!--Other entries omitted -->
</feed>
Your call to SelectSingleNode("name")
also fails due to the presence of this intervening element.
Thus the following code will work and correctly select your entry names:
var xmlDoc = new XmlDocument();
xmlDoc.LoadXml(response); //assuming response is above xml string
var manager = new XmlNamespaceManager(xmlDoc.NameTable);
manager.AddNamespace("atom", @"http://purl.org/atom/ns#");
var parentNode = xmlDoc.DocumentElement.SelectNodes("./atom:entry", manager);
foreach (XmlNode childrenNode2 in parentNode)
{
var name = childrenNode2.SelectSingleNode("./atom:author/atom:name", manager)?.InnerText ?? "";
Console.WriteLine(name);
}
Sample fiddle #1 here.
Incidentally, this can all be done more conveniently with the LINQ to XML API, completely avoiding the need to use XmlNamespaceManager
and XPath and whatnot:
var xmlDoc = XDocument.Parse(response);
var ns = (XNamespace)@"http://purl.org/atom/ns#";
foreach (var element in xmlDoc.Root.Elements(ns + "entry"))
{
var name = element.Element(ns + "author")?.Element(ns + "name")?.Value ?? "";
Console.WriteLine(name);
}
Sample fiddle #2 here.