Reputation: 577
I have been access a database via an API for a number of years and today a change was made but I cannot contact the owners. It appears to be a subtle change that results in my code giving a Null Reference exception.
A file is downloaded, and then I attempt to use the XmlReader
and load it into a dictionary using this code:
Dictionary<decimal, string> dict = new Dictionary<decimal, string>();
using (var file = File.Open(dir + @"\dxcc_matrix.gz", FileMode.Open))
{
using (var zip = new GZipStream(file, CompressionMode.Decompress))
{
using (var xmlReader = XmlReader.Create(zip))
{
var xd = XDocument.Load(xmlReader);
dict = //error occurs here
xd
.Document
.Root
.Element(XName.Get("entities", "http://www.clublog.org/cty/v1.0"))
.Elements(XName.Get("entity", "http://www.clublog.org/cty/v1.0"))
.ToDictionary(
x => (decimal)x.Element(XName.Get("adif", "http://www.clublog.org/cty/v1.0")),
x => x.Element(XName.Get("name", "http://www.clublog.org/cty/v1.0")).Value);
}
}
}
The partial XML file looks like this:
<clublog date="2018-02-13T21:30:11+00:00"
xmlns="https://clublog.org/cty/v1.0">
<entities>
<entity>
<adif>1</adif>
<name>CANADA</name>
<prefix>VE</prefix>
<deleted>FALSE</deleted>
<cqz>5</cqz>
<cont>NA</cont>
<long>-80.00</long>
<lat>45.00</lat>
</entity>
<entity>
<adif>2</adif>
<name>ABU AIL IS</name>
<prefix>A1</prefix>
<deleted>TRUE</deleted>
<cqz>21</cqz>
<cont>AS</cont>
<long>45.00</long>
<lat>12.80</lat>
<end>1991-03-30T23:59:59+00:00</end>
</entity>
<!--Additional entities omitted-->
</entities>
</clublog>
Is there all of a sudden something wrong in my code or is the XML unusable with the current code?
Upvotes: 2
Views: 1518
Reputation: 116786
Your problem is that, in some versions of the XML, the <entity>
and <entities>
elements are in the "http://www.clublog.org/cty/v1.0"
XML namespace, but in others they are in the "https://clublog.org/cty/v1.0"
namespace.
In order to parse either XML version, you need to check for your elements being in either of the two possible namespaces, for instance using the following methods:
public static class AdifDictionaryExtensions
{
public static Dictionary<decimal, string> ExtractAdifDictionary(TextReader reader)
{
Dictionary<decimal, string> dict = new Dictionary<decimal, string>();
using (var xmlReader = XmlReader.Create(reader))
{
var xd = XDocument.Load(xmlReader);
var ns1 = (XNamespace)"http://www.clublog.org/cty/v1.0";
var ns2 = (XNamespace)"https://clublog.org/cty/v1.0";
dict =
xd
.Root
.Elements("entities", ns1, ns2).Single()
.Elements("entity", ns1, ns2)
.ToDictionary(
x => (decimal)x.Elements("adif", ns1, ns2).Single(),
x => x.Elements("name", ns1, ns2).Single().Value);
return dict;
}
}
}
public static class XContainerExtensions
{
public static IEnumerable<XElement> Elements(this XContainer container, string localName, XNamespace nameSpace, params XNamespace[] additionalNamespaces)
{
if (container == null || localName == null)
throw new ArgumentNullException();
var names = new[] { nameSpace }.Concat(additionalNamespaces).Select(ns => ns + localName).ToArray();
return container.Elements().Where(e => names.Any(n => n == e.Name));
}
}
Notes:
You might be thinking of the XML namespaces "http://www.clublog.org/cty/v1.0"
and "https://clublog.org/cty/v1.0"
as actual URLs that might or might not resolve to the same address. From the point of view of XML parsing however, these namespaces are just strings that help to provide unique naming of elements and attributes when combined into large heterogeneous XML documents. (See XML namespace for more explanation.)
When searching a LINQ to XML hierarchy for elements by name using XContainer.Element(XName)
or XContainer.Elements(XName)
, all that matters is whether the local name and namespace have the required local name and namespace using ordinal string comparison.
Despite its name, XName.Get()
doesn't actually perform an http get or any other network operation. It is a factory method that combines two strings into an XName
class for performant equality comparisons.
Sample working .Net fiddle.
Upvotes: 1
Reputation:
This may be caused by searching an element which does not actually exist in the XML.
In that case, the Null reference exception will be thrown. If the change was made in XML itself then this is likely to be the error cause.
Upvotes: 1