pap*_*zzo 5 .net c# xml xmlwriter xmlreader
如果数据在一行上
index=int.Parse(logDataReader.ReadElementContentAsString());
,则
value=double.Parse(logDataReader.ReadElementContentAsString(),
使光标向前移动。如果我取消这些调用,我会看到它在调试中循环了 6 次。
在下面<data>的第一个 ( <logData id="Bravo">) 中只读取了3 个(并且它们是错误的,因为该值用于下一个索引)。在第二个 ( <logData id="Bravo">) 上,所有内容<data>都已读取。
由于该文件是动态创建的(由 XMLwriter),因此无法编辑 xml 并插入换行符。该NewLineChars设置是换行。从 XMLwriter 开始,它实际上只是一行 - 我将它分解以找出它在哪里中断。在浏览器中它正确显示。
如何解决这个问题?
这是我的 XML:
<?xml version="1.0" encoding="utf-8"?>
<log>
<logData id="Alpha">
<data><index>100</index><value>150</value></data>
<data><index>110</index><value>750</value></data>
<data><index>120</index><value>750</value></data>
<data><index>130</index><value>150</value></data>
<data><index>140</index><value>0</value></data>
<data><index>150</index><value>222</value></data>
</logData>
<logData id="Bravo">
<data>
<index>100</index>
<value>25</value>
</data>
<data>
<index>110</index>
<value>11</value>
</data>
<data>
<index>120</index>
<value>1</value>
</data>
<data>
<index>130</index>
<value>25</value></data>
<data>
<index>140</index>
<value>0</value>
</data>
<data>
<index>150</index>
<value>1</value>
</data>
</logData>
</log>
Run Code Online (Sandbox Code Playgroud)
还有我的代码:
static void Main(string[] args)
{
List<LogData> logDatas = GetLogDatasFromFile("singleVersusMultLine.xml");
Debug.WriteLine("Main");
Debug.WriteLine("logData");
foreach (LogData logData in logDatas)
{
Debug.WriteLine($" logData.ID {logData.ID}");
foreach(LogPoint logPoint in logData.LogPoints)
{
Debug.WriteLine($" logData.Index {logPoint.Index} logData.Value {logPoint.Value}");
}
}
Debug.WriteLine("end");
}
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 = int.Parse(logDataReader.ReadElementContentAsString());
// move to value
logDataReader.ReadToFollowing("value");
// read value
var value = double.Parse(logDataReader.ReadElementContentAsString(), CultureInfo.InvariantCulture);
logData.LogPoints.Add(new LogPoint(index, value));
}
}
logDatas.Add(logData);
}
}
return logDatas;
}
public class LogData
{
public string ID { get; }
public List<LogPoint> LogPoints { get; } = new List<LogPoint>();
public LogData (string id)
{
ID = id;
}
}
public class LogPoint
{
public int Index { get; }
public double Value { get; }
public LogPoint ( int index, double value)
{
Index = index;
Value = value;
}
}
Run Code Online (Sandbox Code Playgroud)
你的问题如下。根据该文件为XmlReader.ReadElementContentAsString():
此方法读取开始标签、元素的内容,并将阅读器移过结束元素标签。
并从文档中XmlReader.ReadToFollowing(String):
它将读取器前进到与指定名称匹配的下一个元素,如果找到匹配的元素,则返回 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);
}
}
Run Code Online (Sandbox Code Playgroud)
然后修改你的代码如下:
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;
}
Run Code Online (Sandbox Code Playgroud)
笔记:
总是更喜欢使用XmlReader分别指定本地名称和命名空间的方法,例如XmlReader.ReadToFollowing (String,?String). 当您使用诸如XmlReader.ReadToFollowing(String)接受单个限定名称的方法时,您隐式地硬编码了 XML前缀的选择,这通常不是一个好主意。XML 解析应该独立于前缀选择。
虽然您使用CultureInfo.InvariantCulture语言环境正确解析了 double ,但使用XmlConvert类中的方法来正确处理解析和格式设置会更容易。
XmlReader.ReadSubtree()将XmlReader定位留在EndElement正在读取的元素的节点上,因此您无需ReadToFollowingOrCurrent()事后调用。(ReadSubtree()顺便说一下,避免阅读太少或太多的好用法;通过使用这种方法,可以避免使用 的几个常见错误XmlReader。)
正如您所发现的,使用手动读取 XML 的代码XmlReader应该始终使用格式化和未格式化的 XML 进行单元测试,因为某些错误只会出现在其中一个中。(参见例如此答案,这一个和这一个也针对这样的其它实例。)
工作示例 .Net fiddle在这里。
| 归档时间: |
|
| 查看次数: |
771 次 |
| 最近记录: |