Я никогда не могу предсказывать поведение XMLReader. Какие-либо подсказки относительно понимания?

Это кажется каждый раз, когда я использую XMLReader, я заканчиваю с набором метода проб и ошибок, пытающегося выяснять то, что я собираюсь считать по сравнению с тем, что я читаю по сравнению с тем, что я просто считал. Я всегда понимаю это в конце, но у меня все еще, после использования его многочисленные времена, кажется, нет твердого схватывания того, что на самом деле делает XMLReader, когда я вызываю различные функции. Например, когда я называю Read первым разом, если он читает, элемент запускают тег, это теперь в конце тега элементов, или готовый начать читать атрибуты элемента? Это знает значения атрибутов уже, если я называю GetAttribute? Что произойдет, если я назову ReadStartElement в этой точке? Это будет заканчивать читать элемент запуска или искать следующий, пропуская все атрибуты? Что, если я хочу считать много элементов - что является лучшим способом попытаться считать следующий элемент и определить, каково его имя. Будет 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 Вы получаете ошибку: "'Элементом' является недопустимый XmlNodeType". попытка к ReadElementString, который я не могу вполне объяснить.

Следующая проблема, читая первый элемент части, ReadElementString, кажется, располагает читателя в начале следующего элемента части после чтения. Это вызывает читателя. Читайте в начале следующего цикла для перескакивания через следующий элемент части, переходящий право на последний элемент части. Таким образом, окончательный результат этого кода:

Код части 01a: Правая рука

Код части 03: Большой палец ноги

Это - главный пример запутывающего поведения XMLReader, который я пытаюсь понять.

15
задан BlueMonkMN 24 January 2010 в 14:44
поделиться

2 ответа

Мое последнее решение (которое работает в моем текущем случае) - это придерживаться 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();
}

После Read, 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, чтобы избежать чтения конечного элемента и пропуска в начало следующего элемента (пусть следующая функция 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);
   }
}
3
ответ дан 1 December 2019 в 05:07
поделиться
-

Вот в чем дело... Я написал достаточное количество кода сериализации (в том числе много обработки xml), и нахожусь в точно в той же лодке, что и вы. Поэтому у меня есть очень простое руководство: don't.

Я с удовольствием использую XmlWriter как способ быстро написать xml, но я бы прогулялся по горячим углям, прежде чем выбрать реализацию IXmlSerializable в другой раз - я бы просто написал отдельную DTO и наложил на нее данные; это также означает, что схема (для "mex", "wsdl" и т.д.) приходит бесплатно.

5
ответ дан 1 December 2019 в 05:07
поделиться
Другие вопросы по тегам:

Похожие вопросы: