Ваша проблема заключается в следующем. Согласно документации для XmlReader.ReadElementContentAsString()
:
Этот метод считывает начальный тег, содержимое элемента и перемещает считыватель на тег конечного элемента.
А из документации для XmlReader.ReadToFollowing(String)
:
Он продвигает читателя к следующему элементу next , который соответствует указанному имени, и возвращает true, если соответствующий элемент найден.
Таким образом, после вызова ReadElementContentAsString()
, поскольку считыватель переведен на следующий узел, он может уже располагаться на следующем <value>
или <data>
узле. Затем, когда вы вызываете ReadToFollowing()
, этот элементный узел пропускается , потому что метод безоговорочно переходит к следующему узлу с правильным именем. Но если XML имеет отступ, то следующий узел сразу после вызова ReadElementContentAsString()
будет узлом XmlNodeType.Whitespace
, защищающим от этой ошибки.
Решение состоит в том, чтобы проверить, правильно ли установлен считыватель после вызова ReadElementContentAsString()
. Сначала введите следующий метод расширения:
public static class XmlReaderExtensions
{
public static bool ReadToFollowingOrCurrent(this XmlReader reader, string localName, string namespaceURI)
{
if (reader == null)
throw new ArgumentNullException(nameof(reader));
if (reader.NodeType == XmlNodeType.Element && reader.LocalName == localName && reader.NamespaceURI == namespaceURI)
return true;
return reader.ReadToFollowing(localName, namespaceURI);
}
}
Затем измените ваш код следующим образом:
public static List<LogData> GetLogDatasFromFile(string xmlFile)
{
List<LogData> logDatas = new List<LogData>();
using (XmlReader reader = XmlReader.Create(xmlFile))
{
// move to next "logData"
while (reader.ReadToFollowing("logData", ""))
{
var logData = new LogData(reader.GetAttribute("id"));
using (var logDataReader = reader.ReadSubtree())
{
// inside "logData" subtree, move to next "data"
while (logDataReader.ReadToFollowing("data", ""))
{
// move to index
logDataReader.ReadToFollowing("index", "");
// read index
var index = XmlConvert.ToInt32(logDataReader.ReadElementContentAsString());
// move to value
logDataReader.ReadToFollowingOrCurrent("value", "");
// read value
var value = XmlConvert.ToDouble(logDataReader.ReadElementContentAsString());
logData.LogPoints.Add(new LogPoint(index, value));
}
}
logDatas.Add(logData);
}
}
return logDatas;
}
Примечания:
Всегда предпочитайте использовать XmlReader
методы, в которых локальное имя и пространство имен указываются отдельно, например, XmlReader.ReadToFollowing (String, String)
. Когда вы используете метод, такой как XmlReader.ReadToFollowing(String)
, который принимает одно квалифицированное имя, вы неявно жестко кодируете выбор префикса XML , что, как правило, не очень хорошая идея. Синтаксический анализ XML не должен зависеть от выбора префикса.
Несмотря на то, что вы правильно проанализировали ваш дубль, используя язык CultureInfo.InvariantCulture
, еще проще использовать методы класса XmlConvert
для правильной обработки синтаксического анализа и форматирования.
XmlReader.ReadSubtree()
оставляет XmlReader
позиционированным на EndElement
узле читаемого элемента, поэтому вам не нужно вызывать ReadToFollowingOrCurrent()
после этого. (Хорошее использование ReadSubtree()
, чтобы не читать слишком мало или слишком много, кстати; с помощью этого метода можно избежать нескольких частых ошибок с XmlReader
.)
Как вы обнаружили, код, который вручную читает XML с использованием XmlReader
, всегда должен подвергаться модульному тестированию как с отформатированным, так и с неформатированным XML, поскольку определенные ошибки могут возникать только с одним или другим. (См., Например, этот ответ , этот и этот также для других примеров такого.)
Рабочий образец. Net fiddle here .