XmlReader ReadStartElement вызывает XmlException

Я пишу для чтения файлов с использованием XmlReader в проекте Silverlight. Тем не менее, я получаю некоторые ошибки (особенно в отношении метода XmlReader.ReadStartElement), и это заставляет меня поверить, что я неправильно понял, как использовать его где-то по пути.

В основном, вот пример формата Xml, который я использую:

<?xml version="1.0" encoding="utf-8" standalone="no"?>
<root>
    <EmptyElement />
    <NonEmptyElement Name="NonEmptyElement">
        <SubElement Name="SubElement" />
    </NonEmptyElement>
</root>

А вот пример кода, который используется так же, как и я:

public void ReadData(XmlReader reader)
{
    // Move to root element
    reader.ReadStartElement("root");

    // Move to the empty element
    reader.ReadStartElement("EmptyElement");

    // Read any children
    while(reader.ReadToNextSibling("SubEmptyElement"))
    {
        // ...
    }

    // Read the end of the empty element
    reader.ReadEndElement();

    // Move to the non empty element
    reader.ReadStartElement("NonEmptyElement");    // NOTE: This is where I get the error.

    // ...
}

Итак, по сути, я просто пытаюсь прочитать каждый элемент и все содержащиеся в нем дети. Ошибка, которую я получаю в выделенной точке, заключается в следующем:

Описание ошибки

[Xml_InvalidNodeType] Аргументы: нет,10,8 Строки ресурсов отладки недоступны. Часто ключ и аргументы предоставляют достаточную информацию для диагностики проблемы. См. http://go.microsoft.com/fwlink/?linkid=106663&Version=4.0.51204.0&File=System.Xml.dll&Key=Xml_InvalidNodeType

Трассировка стека ошибок

в System.Xml.XmlReader.ReadStartElement(имя строки) в ----------------

Любой совет или направление по этому вопросу будет принята с благодарностью.

РЕДАКТИРОВАТЬ Так как этот читатель должен быть достаточно общим, можно предположить, что Xml может содержать элементы, которые являются дочерними для EmptyElement. Таким образом, попытка чтения любых SubEmptyElements должна быть действительной.

1 ответ

Решение

<SubElement/> это не брат или сестра <EmptyElement>, так <NonEmptyElement> будет полностью пропущен, и ваш звонок ReadEndElement() будет читать конечный элемент </root>, Когда вы попытаетесь впоследствии прочитать "NonEmptyElement", не останется никаких элементов, и вы получите исключение XmlException: {"None" является недопустимым XmlNodeType. Строка 8, позиция 1."}

Обратите внимание, что с <EmptyElement/> пусто, когда вы ReadStartElement("EmptyElement"), вы прочитаете весь элемент, и вам не нужно будет использовать ReadEndElement().

Я также рекомендую вам настроить параметры чтения на IgnoreWhitespace (если вы этого еще не сделали), чтобы избежать каких-либо сложностей, возникающих при чтении (незначительных) пробельных текстовых узлов, когда вы их не ожидаете.

Попробуйте переместить Read of NonEmptyElement вверх:

public static void ReadData(XmlReader reader)
{
    reader.ReadStartElement("root");

    reader.ReadStartElement("EmptyElement");

    reader.ReadStartElement("NonEmptyElement");

    while (reader.ReadToNextSibling("SubEmptyElement"))
    {
        // ...
    }

    reader.ReadEndElement(/* NonEmptyElement */);

    reader.ReadEndElement(/* root */);
    // ...
}

Если вы просто хотите пропустить что-нибудь в <EmptyElement>независимо от того, является ли он фактически пустым, используйте ReadToFollowing:

public static void ReadData(XmlReader reader)
{
    reader.ReadStartElement("root");

    reader.ReadToFollowing("NonEmptyElement");

    Console.WriteLine(reader.GetAttribute("Name"));

    reader.ReadStartElement("NonEmptyElement");

    Console.WriteLine(reader.GetAttribute("Name"));
    while (reader.ReadToNextSibling("SubEmptyElement"))
    {
        // ...
    }

    reader.ReadEndElement(/* NonEmptyElement */);

    reader.ReadEndElement(/* root */);
    // ...
}

Обновление: вот более полный пример с более четкой моделью данных. Может быть, это ближе к тому, что вы просите.

XMLFile1.xml:

<?xml version="1.0" encoding="utf-8" standalone="no"?>
<root>
  <Person Type="Homeless"/>
  <Person Type="Developer">
    <Home Type="Apartment" />
  </Person>
  <Person Type="Banker">
    <Home Type="Apartment"/>
    <Home Type="Detached"/>
    <Home Type="Mansion">
      <PoolHouse/>
    </Home>
  </Person>
</root>

Program.cs:

using System;
using System.Xml;

namespace ConsoleApplication6
{
    internal class Program
    {
        public static void ReadData(XmlReader reader)
        {
            reader.ReadStartElement("root");

            while (reader.IsStartElement("Person"))
            {
                ReadPerson(reader);
            }

            reader.ReadEndElement( /* root */);
        }

        public static void ReadPerson(XmlReader reader)
        {
            Console.WriteLine(reader.GetAttribute("Type"));
            bool isEmpty = reader.IsEmptyElement;
            reader.ReadStartElement("Person");
            while (reader.IsStartElement("Home"))
            {
                ReadHome(reader);
            }
            if (!isEmpty)
            {
                reader.ReadEndElement( /* Person */);
            }
        }

        public static void ReadHome(XmlReader reader)
        {
            Console.WriteLine("\t" + reader.GetAttribute("Type"));
            bool isEmpty = reader.IsEmptyElement;
            reader.ReadStartElement("Home");

            if (!isEmpty)
            {
                reader.Skip();
                reader.ReadEndElement( /* Home */);
            }
        }

        private static void Main(string[] args)
        {
            var settings = new XmlReaderSettings { IgnoreWhitespace = true };
            using (var xr = XmlReader.Create("XMLFile1.xml", settings))
            {
                ReadData(xr);
            }
            Console.ReadKey();
        }
    }
}
Другие вопросы по тегам