Ответ 1
Моим последним решением (которое работает для моего текущего случая) является использование Read(), IsStartElement (name) и GetAttribute (name) при реализации конечного автомата.
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, если вы просто прочитали начальный элемент (проверяя имя элемента чтения), и вы можете сразу получить доступ ко всем атрибутам этого элемента. Если все, что вам нужно прочитать, это элементы и атрибуты, это довольно просто.
Edit Новый пример, поставленный в вопросе, создает некоторые другие проблемы. Правильный способ чтения этого 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, чтобы избежать чтения конечного элемента и пропустить его в начало следующего элемента (пусть следующий Read() пропустит конечный элемент, чтобы он не пропустил следующий запуск элемент). Тем не менее это кажется несколько запутанным и потенциально ненадежным, но оно работает для этого случая.
После некоторой дополнительной мысли, я считаю, что XMLReader просто слишком запутан, если вы используете какие-либо методы для чтения контента, отличного от метода Read. Я думаю, что это намного проще, если вы ограничитесь методом 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);
}
}