Ошибка синтаксического анализа XDocument


Я уже несколько лет получаю доступ к базе данных через API, и сегодня были внесены изменения, но я не могу связаться с владельцами. Это, по-видимому, тонкое изменение, которое приводит к тому, что мой код дает исключение Null Reference. Файл загружается, а затем я пытаюсь использовать XmlReader и загрузить его в словарь, используя следующий код:

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);
        }
    }
}

Частичный XML-файл выглядит следующим образом:

<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>

Вдруг что-то не так в моем коде или XML непригоден для использования с текущим кодом? код?

3 2

3 ответа:

Ваша проблема заключается в том, что в некоторых версиях XML элементы <entity> и <entities> находятся в пространстве имен "http://www.clublog.org/cty/v1.0" XML, но в других они находятся в пространстве имен "https://clublog.org/cty/v1.0".

Чтобы проанализировать любую из версий XML, необходимо проверить, находятся ли ваши элементы в любом из двух возможных пространств имен, например, используя следующие методы:

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));
    }
}

Примечания:

  • Возможно, вы думаете о пространствах имен XML "http://www.clublog.org/cty/v1.0" и "https://clublog.org/cty/v1.0" как о реальных URL-адресах, которые могут быть или не быть решите обратиться по тому же адресу. Однако с точки зрения синтаксического анализа XML эти пространства имен являются просто строками, которые помогают обеспечить уникальное именование элементов и атрибутов при объединении в большие разнородные XML-документы. (См. пространство имен XML для более подробного объяснения.)

    При поиске элементов иерархии LINQ to XML по имени с помощью XContainer.Element(XName) или XContainer.Elements(XName), все, что имеет значение, является ли локальным именем и пространством имен иметь требуемое локальное имя и пространство имен, используя сравнение порядковых строк.

  • Несмотря на свое название, XName.Get() фактически не выполняет HTTP get или любую другую сетевую операцию. Это фабричный метод, который объединяет две строки в одну.XName класс для сравнения перформантных равенств.

Пример работы .Net fiddle .

Это может быть вызвано поиском элемента, который фактически не существует в XML.
в этом случае будет выдано исключение Null reference. Если изменение было сделано в самом XML, то это, вероятно, будет причиной ошибки.

XName.Get возвращает объект XName, который имеет значение null, поскольку url http://www.clublog.org/cty/v1.0 возвращает ошибку 404. Единственный способ исправить это-найти расположение нового URL-адреса.