XmlReader与换行符的行为不同

时间:2018-04-28 17:15:36

标签: c# .net xml xmlreader xmlwriter

如果数据在一行上 elif ... elseif 使光标向前移动。如果我接受这些调用,我会在调试中看到它循环6次。

在下面的第一个(index=int.Parse(logDataReader.ReadElementContentAsString());)中只读取了3个value=double.Parse(logDataReader.ReadElementContentAsString(),(并且它们的值是下一个索引的值)。在第二个(<data>)上读取所有<logData id="Bravo">

不能选择编辑xml并放入换行符,因为该文件是动态创建的(通过XMLwriter)。 <logData id="Bravo">设置是换行符。从XMLwriter开始,它实际上只是一行 - 我把它分解出来以确定它在哪里破碎。在浏览器中显示正确。

如何解决此问题?

这是我的XML:

<data>

我的代码:

NewLineChars

3 个答案:

答案 0 :(得分:4)

您的问题如下。根据{{​​1}}的{​​{3}}:

  

此方法读取开始标记,元素的内容,并将读者移动过去结束元素标记。

来自XmlReader.ReadElementContentAsString()的{​​{3}}:

  

它使读者前进到与指定名称匹配的 next 跟随元素,如果找到匹配的元素则返回true。

因此,在调用XmlReader.ReadToFollowing(String)之后,由于读者已进入下一个节点,因此它可能已经位于下一个ReadElementContentAsString()<value>节点上。然后,当您致电<data>时,此元素节点被跳过,因为该方法无条件地移动到具有正确名称的 next 节点。但是如果XML是缩进的,那么在调用ReadToFollowing()之后的下一个节点将是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; } 方法,其中单独指定本地名称和命名空间,例如documentation。当您使用接受单个限定名称的documentation等方法时,您隐式硬编码XML 前缀的选择,这通常不是一个好主意。 XML解析应独立于前缀选择。

  • 在使用XmlReader语言环境正确解析双精度语时,使用XmlReader.ReadToFollowing (String, String)类中的方法更容易正确处理解析和格式化。

  • XmlReader.ReadToFollowing(String)CultureInfo.InvariantCulture位于位于正在读取的元素的XmlReader节点上,因此您无需拨打电话之后EndElement。 (很好地使用ReadToFollowingOrCurrent()来避免读取太少或太多;通过使用此方法,可以避免使用ReadSubtree()的几个常见错误。)

  • 正如您所发现的那样,使用XmlReader手动读取XML的代码应该始终使用格式化和未格式化的XML进行单元测试,因为某些错误只会出现在一个或另一个错误中。 (例如,请参阅XmlConvertXmlReader.ReadSubtree()this answer等其他示例。)

工作样本.Net小提琴this one

答案 1 :(得分:1)

事实上,我在另一个问题中提供的代码是错误的。 ReadToFollowing将使用此名称读取下一个元素,即使它的光标已经位于具有此名称的元素上。当有空格时 - 在您阅读index之后,光标移动到该空白并且ReadToFollowing("value")按预期工作。但是,如果没有空格,则游标已经在value节点上,因此ReadToFollowing("value")将读取后续“数据”节点中的下一个“值”。

我认为以下是一种更安全的方法:

public static List<LogData> GetLogDatasFromFile(string xmlFile) {
    List<LogData> logDatas = new List<LogData>();

    using (XmlReader reader = XmlReader.Create(xmlFile)) {
        LogData currentData = null;
        while (reader.Read()) {
            if (reader.IsStartElement("logData")) {
                // we are positioned on start of logData
                if (currentData != null)
                    logDatas.Add(currentData);
                currentData = new LogData(reader.GetAttribute("id"));
            }
            else if (reader.IsStartElement("data")) {
                // we are on start of "data"
                // we always have "currentData" at this point                        
                Debug.Assert(currentData != null);
                reader.ReadToFollowing("index");
                var index = int.Parse(reader.ReadElementContentAsString());
                // check if we are not already on "value"
                if (!reader.IsStartElement("value"))
                    reader.ReadToFollowing("value");
                var value = double.Parse(reader.ReadElementContentAsString(), CultureInfo.InvariantCulture);
                currentData.LogPoints.Add(new LogPoint(index, value));
            }
        }

        if (currentData != null)
            logDatas.Add(currentData);
    }

    return logDatas;
}

答案 2 :(得分:0)

我发现了一个修复但对我来说不是一个可接受的答案。对于换行符,XMLreader的行为不应该不同。

XmlWriter中,这会在文字中添加换行符:

XmlWriterSettings xmlWriterSettings = new XmlWriterSettings();
xmlWriterSettings.NewLineOnAttributes = true;
xmlWriterSettings.Indent = true;
using (XmlWriter xmlWriter = XmlWriter.Create(fileNameXML, xmlWriterSettings))
{

我发现了here