似乎每当我使用XMLReader时,我最终会尝试一些试验和错误,试图找出我要阅读的内容,而不是我正在阅读的内容与我刚刚阅读的内容。我总是把它弄清楚,但是在我多次使用它之后,我似乎并没有牢牢掌握当我调用各种函数时XMLReader实际上在做什么。例如,当我第一次调用Read时,如果它读取元素开始标记,它现在是在元素标记的末尾,还是准备开始读取元素的属性?如果我调用GetAttribute,它是否知道属性的值?如果我此时调用ReadStartElement会发生什么?它会完成读取start元素,还是寻找下一个元素,跳过所有属性?如果我想阅读许多元素怎么办?尝试阅读下一个元素并确定其名称的最佳方法是什么。将Read继续IsStartElement工作,或者IsStartElement将返回有关我刚读过的元素后节点的信息吗?
正如您所看到的,我真的不了解XMLReader在读取的各个阶段所处的位置以及各种读取函数对其状态的影响。是否有一些我根本没注意到的简单模式?
这是问题的另一个例子(取自回复):
string input = "<machine code=\"01\">The Terminator" +
"<part code=\"01a\">Right Arm</part>" +
"<part code=\"02\">Left Arm</part>" +
"<part code=\"03\">Big Toe</part>" +
"</machine>";
using (System.IO.StringReader sr = new System.IO.StringReader(input))
{
using (XmlTextReader reader = new XmlTextReader(sr))
{
reader.WhitespaceHandling = WhitespaceHandling.None;
reader.MoveToContent();
while(reader.Read())
{
if (reader.Name.Equals("machine") && (reader.NodeType == XmlNodeType.Element))
{
Console.Write("Machine code {0}: ", reader.GetAttribute("code"));
Console.WriteLine(reader.ReadElementString("machine"));
}
if(reader.Name.Equals("part") && (reader.NodeType == XmlNodeType.Element))
{
Console.Write("Part code {0}: ", reader.GetAttribute("code"));
Console.WriteLine(reader.ReadElementString("part"));
}
}
}
}
第一个问题,机器节点被完全跳过。 MoveToContent似乎移动到机器元素的内容,导致它永远不会被解析。此外,如果您跳过MoveToContent,则会收到错误:“'Element'是无效的XmlNodeType。”尝试ReadElementString,我无法解释。
接下来的问题是,在读取第一部分元素时,ReadElementString似乎在阅读后将阅读器定位在下一个部分元素的开头。这会导致reader.Read在下一个循环的开头跳过下一个跳转到最后一个part元素的part元素。所以这段代码的最终输出是:
零件代码01a:右臂
零件代码03:大脚趾
这是我试图理解的XMLReader混淆行为的一个主要例子。
答案 0 :(得分:5)
这就是......我写了大量的序列化代码(包括很多xml处理代码),而且我发现自己和完全在同一条船上。因此,我有一个非常简单的指导:不。
我很乐意使用XmlWriter
作为快速编写xml的方法,但是在选择另一次实现IXmlSerializable
之前我会走过热煤 - 我只是写一个单独的{{ 1}}并将数据映射到那个;它还意味着模式(对于“mex”,“wsdl”等)是免费的。
答案 1 :(得分:3)
我的最新解决方案(这适用于我目前的情况下)正在实施的状态机读(),IsStartElement(名称)和的getAttribute(名称)要坚持。
using (System.Xml.XmlReader xr = System.Xml.XmlTextReader.Create(stm))
{
employeeSchedules = new Dictionary<string, EmployeeSchedule>();
EmployeeSchedule emp = null;
WeekSchedule sch = null;
TimeRanges ranges = null;
TimeRange range = null;
while (xr.Read())
{
if (xr.IsStartElement("Employee"))
{
emp = new EmployeeSchedule();
employeeSchedules.Add(xr.GetAttribute("Name"), emp);
}
else if (xr.IsStartElement("Unavailable"))
{
sch = new WeekSchedule();
emp.unavailable = sch;
}
else if (xr.IsStartElement("Scheduled"))
{
sch = new WeekSchedule();
emp.scheduled = sch;
}
else if (xr.IsStartElement("DaySchedule"))
{
ranges = new TimeRanges();
sch.daySchedule[int.Parse(xr.GetAttribute("DayNumber"))] = ranges;
ranges.Color = ParseColor(xr.GetAttribute("Color"));
ranges.FillStyle = (System.Drawing.Drawing2D.HatchStyle)
System.Enum.Parse(typeof(System.Drawing.Drawing2D.HatchStyle),
xr.GetAttribute("Pattern"));
}
else if (xr.IsStartElement("TimeRange"))
{
range = new TimeRange(
System.Xml.XmlConvert.ToDateTime(xr.GetAttribute("Start"),
System.Xml.XmlDateTimeSerializationMode.Unspecified),
new TimeSpan((long)(System.Xml.XmlConvert.ToDouble(xr.GetAttribute("Length")) * TimeSpan.TicksPerHour)));
ranges.Add(range);
}
}
xr.Close();
}
阅读后,IsStartElement将返回true,如果你只是读一开始元素(optinally检查元素读的名称),并可以立即访问该元素的所有属性。如果您需要阅读的只是元素和属性,这非常简单。
修改强> 问题中发布的新示例带来了一些其他挑战。读取XML的正确方法似乎是这样的:
using (System.IO.StringReader sr = new System.IO.StringReader(input))
{
using (XmlTextReader reader = new XmlTextReader(sr))
{
reader.WhitespaceHandling = WhitespaceHandling.None;
while(reader.Read())
{
if (reader.Name.Equals("machine") && (reader.NodeType == XmlNodeType.Element))
{
Console.Write("Machine code {0}: ", reader.GetAttribute("code"));
Console.WriteLine(reader.ReadString());
}
if(reader.Name.Equals("part") && (reader.NodeType == XmlNodeType.Element))
{
Console.Write("Part code {0}: ", reader.GetAttribute("code"));
Console.WriteLine(reader.ReadString());
}
}
}
}
您必须使用ReadString,而不是ReadElementString为了避免读取结束元素和跳跃到下一个元素的开始(让下面的阅读()跳过结束元素,因此不会在下次启动时跳过元件)。这仍然有点混乱,可能不可靠,但它适用于这种情况。
经过一些额外的考虑之后,我的观点是XMLReader太混乱如果你使用任何方法来读取除Read方法之外的内容。我认为如果您将自己局限于Read方法以从XML流中读取,则会更加简单。下面是它如何与新的工作,例如(再次,似乎IsStartElement,和的getAttribute读取的关键方法,你结束了一个状态机):
while(reader.Read())
{
if (reader.IsStartElement("machine"))
{
Console.Write("Machine code {0}: ", reader.GetAttribute("code"));
}
if(reader.IsStartElement("part"))
{
Console.Write("Part code {0}: ", reader.GetAttribute("code"));
}
if (reader.NodeType == XmlNodeType.Text)
{
Console.WriteLine(reader.Value);
}
}