我永远无法预测XMLReader的行为。 理解的任何提示?

似乎每次我使用XMLReader时,我最终会进行一系列的试验和错误,试图找出我要阅读的内容,而不是我正在阅读的内容与我刚刚阅读的内容。 我总是把它弄清楚,但是在我多次使用之后,我仍然没有牢牢掌握XMLReader在调用各种函数时实际在做什么。 例如,当我第一次调用Read时,如果它读取元素开始标记,它现在是在元素标记的末尾,还是准备开始读取元素的属性? 如果我调用GetAttribute,它是否知道属性的值? 如果我此时调用ReadStartElement会发生什么? 它会完成读取start元素,还是寻找下一个元素,跳过所有属性? 如果我想阅读许多元素怎么办?尝试阅读下一个元素并确定其名称的最佳方法是什么。 将Read继续IsStartElement工作,或者IsStartElement将返回有关我刚读过的元素之后的节点的信息吗?

正如您所看到的,我真的不了解XMLReader在读取的各个阶段所处的位置以及各种读取函数对其状态的影响。 是否有一些我根本没注意到的简单模式?

这是问题的另一个例子(取自回复):

string input = "The Terminator" + "Right Arm" + "Left Arm" + "Big Toe" + ""; 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混淆行为的一个主要例子。

这就是……我写了大量的序列化代码(包括很多xml处理代码),我发现自己与你完全一样。 因此,我有一个非常简单的指导: 不要

我很乐意使用XmlWriter作为快速编写xml的方法,但是在选择另一次实现IXmlSerializable之前我会走过热煤 – 我只是编写一个单独的DTO并将数据映射到那个; 它还意味着架构(“mex”,“wsdl”等)免费提供。

我的最新解决方案(适用于我目前的情况)是在实现状态机时坚持使用Read(),IsStartElement(name)和GetAttribute(name)。

 using (System.Xml.XmlReader xr = System.Xml.XmlTextReader.Create(stm)) { employeeSchedules = new Dictionary(); 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(); } 

在Read之后,如果您只是读取一个start元素(最后检查读取的元素的名称),IsStartElement将返回true,并且您可以立即访问该元素的所有属性。 如果您需要阅读的只是元素和属性,那么这非常简单。

编辑问题中发布的新示例带来了一些其他挑战。 读取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,以避免读取end元素并跳到下一个元素的开头(让下面的Read()跳过end元素,这样就不会跳过下一个start元素)。 这仍然有点混乱,可能不可靠,但它适用于这种情况。

经过一些额外的考虑,我的观点是, 如果你使用任何方法来读取除Read方法之外的内容,那么XMLReader就太混乱了。 我认为如果你将自己局限于Read方法以从XML流中读取它会更简单。 以下是它如何与新示例一起工作(再次,似乎IsStartElement,GetAttribute和Read是关键方法,最终你得到一个状态机):

 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); } }