Проблема пробелов в c # с XmlReader - PullRequest
5 голосов
/ 22 февраля 2012

У меня есть простой xml

<data>
    <node1>value1</node1>
    <node2>value2</node2>
</data>

Я использую IXmlSerializable для чтения и записи такого xml с DTO.Следующий код работает просто отлично

XmlReader reader;
...
while( reader.Read() ){
    Console.Write( reader.ReadElementContentAsString() );
}
// outputs value1value2

Однако, если пропущены пробелы в xml, т.е.

<data>
    <node1>value1</node1><node2>value2</node2>
</data>

или я использую XmlReaderSettings.IgnoreWhitespace = true;, код выводит только «значение1», игнорируявторой узел.Когда я печатаю узлы, через которые проходит анализатор, я вижу, что ReadElementContentAsString перемещает указатель на EndElement из node2, но я не понимаю, почему это должно происходить или как это исправить.

Это возможная ошибка реализации XML-парсера?

==============================================

Вот пример кода и 2 примера xml, которые дают разные результаты

string homedir = Path.GetDirectoryName(Application.ExecutablePath);
string xml = Path.Combine( homedir, "settings.xml" );

FileStream stream = new FileStream( xml, FileMode.Open );

XmlReaderSettings readerSettings = new XmlReaderSettings();
readerSettings.IgnoreWhitespace = false;
XmlReader reader = XmlTextReader.Create( stream, readerSettings );

while( reader.Read() ){

    if ( reader.MoveToContent() == XmlNodeType.Element && reader.Name != "data" ){

        System.Diagnostics.Trace.WriteLine(
            reader.NodeType 
            + " "
            + reader.Name
            + " " 
            + reader.ReadElementContentAsString()
        );
    }
}

stream.Close(); 

1.) Settings.xml

<?xml version="1.0"?>
<data>
    <node-1>value1</node-1>
    <node-2>value2</node-2>
</data>

2.) Settings.xml

<?xml version="1.0"?>
<data>
    <node-1>value1</node-1><node-2>value2</node-2>
</data>

с использованием (1) отпечатков

Element node-1 value1
Element node-2 value2

с использованием (2) отпечатков

Element node-1 value1

Ответы [ 4 ]

3 голосов
/ 22 февраля 2012

Согласно документации по IgnoreWhitespace, новая строка не считается незначительной.

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

XmlReaderSettings.IgnoreWhitespace

1 голос
/ 09 июля 2015

Если вы хотите, чтобы XmlReader не считывал пробелы, вы должны инициализировать XmlReader с настройками следующим образом:

XmlReaderSettings settings = new XmlReaderSettings();
settings.IgnoreWhitespace = true;
XmlReader xrd = XmlReader.Create(@"file.xml", settings);

он работает для меня в файле xml структуры, которую вы разместили:

<data>
    <node1>value1</node1>
    <node2>value2</node2>
</data>
1 голос
/ 08 апреля 2014

Это не так надежно, как ответ Луки, но я нашел следующую схему полезной с разумным «предсказуемым» XML (только изменения в пробелах и значениях).Рассмотрим:

string homedir = Path.GetDirectoryName(Application.ExecutablePath);
string xml = Path.Combine( homedir, "settings.xml" );

FileStream stream = new FileStream( xml, FileMode.Open );

XmlReaderSettings readerSettings = new XmlReaderSettings();
readerSettings.IgnoreWhitespace = false;
XmlReader reader = XmlTextReader.Create( stream, readerSettings );

while( reader.Read() ){

    if ( reader.MoveToContent() == XmlNodeType.Element && reader.Name != "data" ){
        string name = reader.Name;
        string value = null;
        if (!reader.IsEmptyElement) 
        {
          reader.Read(); // advances reader to element content
          value = reader.ReadContentAsString(); // advances reader to endelement
        }
        reader.Read(); // advance reader to element content
        System.Diagnostics.Trace.WriteLine(
            reader.NodeType 
            + " "
            + name
            + " " 
            + value
        );
    }
}

stream.Close(); 

В более общем смысле вместо reader.ReadElementContent*() используйте reader.Read(), а затем reader.ReadContent*().

1 голос
/ 22 февраля 2012

Бывает, что reader.Read() читает символ пробела. Игнорируя пробелы, та же самая инструкция читает второй элемент ("gnam" - XML-токен), действительно приводя указатель к элементу node2 .

Отладка свойств reader до и после методов, вызываемых в вашем примере. Проверьте NodeType и Значение свойства. Также дайте проверку для метода MoveToContent , это очень полезно.

Прочтите документацию по всем этим методам и свойствам, и вы в конечном итоге узнаете, как работает класс XmlReader , и как вы используете его для своих целей. Здесь - это первый результат Google: он содержит очень явный пример.

Я закончил со следующей (не полной) схемой:

private static void ReadXmlExt(XmlReader xmlReader, IXmlSerializableExt xmlSerializable, ReadElementDelegate readElementCallback)
{
    bool isEmpty;

    if (xmlReader == null)
        throw new ArgumentNullException("xmlReader");
    if (readElementCallback == null)
        throw new ArgumentNullException("readElementCallback");

    // Empty element?
    isEmpty = xmlReader.IsEmptyElement;
    // Decode attributes
    if ((xmlReader.HasAttributes == true) && (xmlSerializable != null))
        xmlSerializable.ReadAttributes(xmlReader);

    // Read the root start element
    xmlReader.ReadStartElement();

    // Decode elements
    if (isEmpty == false) {
        do {
            // Read document till next element
            xmlReader.MoveToContent();

            if (xmlReader.NodeType == XmlNodeType.Element) {
                string elementName = xmlReader.LocalName;

                // Empty element?
                isEmpty = xmlReader.IsEmptyElement;

                // Decode child element
                readElementCallback(xmlReader);
                xmlReader.MoveToContent();

                // Read the child end element (not empty)
                if (isEmpty == false) {
                    // Delegate check: it has to reach and end element
                    if (xmlReader.NodeType != XmlNodeType.EndElement)
                        throw new InvalidOperationException(String.Format("not reached the end element"));
                    // Delegate check: the end element shall correspond to the start element before delegate
                    if (xmlReader.LocalName != elementName)
                        throw new InvalidOperationException(String.Format("not reached the relative end element of {0}", elementName));

                    // Child end element
                    xmlReader.ReadEndElement();
                }
            } else if (xmlReader.NodeType == XmlNodeType.Text) {
                if (xmlSerializable != null) {
                    // Interface
                    xmlSerializable.ReadText(xmlReader);
                    Debug.Assert(xmlReader.NodeType != XmlNodeType.Text, "IXmlSerializableExt.ReadText shall read the text");
                } else
                    xmlReader.Skip();   // Skip text
            }
        } while (xmlReader.NodeType != XmlNodeType.EndElement);
    }
}
...