2010-01-23 1 views
15

Кажется, каждый раз, когда я использую XMLReader, я получаю кучу проб и ошибок, пытаясь понять, что я собираюсь читать, и то, что я читаю, и то, что я только что прочитал. Я всегда понимаю это в конце, но я все же, после использования его несколько раз, похоже, не имеет четкого понимания того, что делает XMLReader, когда я называю различные функции. Например, когда я вызываю Read в первый раз, если он читает начальный тег элемента, находится ли он в конце тега элемента или готов начать чтение атрибутов элемента? Знает ли он значения атрибутов, если я вызываю GetAttribute? Что произойдет, если я назову ReadStartElement? По завершении чтения элемента запуска или поиска следующего, пропустив все атрибуты? Что делать, если я хочу прочитать несколько элементов - как лучше всего попытаться прочитать следующий элемент и определить, как его зовут. Будет ли Read, за которым следует IsStartElement, или IsStartElement будет возвращать информацию об узле, следующем за элементом, который я только что прочитал?Я никогда не могу предсказать поведение XMLReader. Любые советы по пониманию?

Как вы можете видеть, мне действительно не хватает понимания того, где находится 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: Big Toe

Это яркий пример confusign поведения XMLReader, что я пытаюсь Понимаю.

ответ

3

Мое последнее решение (которое работает для моего текущего случая) заключается в использовании 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 возвращает истину, если вы только что прочитали стартовый элемент (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, чтобы избежать чтения конечного элемента и пропуск в начале следующего элемента (пусть следующий Read() пропустите конечный элемент, чтобы он не пропустил следующий элемент запуска). Тем не менее это кажется несколько запутанным и потенциально ненадежным, но оно работает для этого случая.

После некоторой дополнительной мысли, я считаю, что 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); 
    } 
} 
+0

+1 для stick with Read() –

5

Вот что ... Я написал довольно много кода сериализации (включая много обработки XML), и я оказался в ровно той же лодкой, что и вы. У меня очень простое руководство, поэтому: не.

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

+0

Можете ли вы сказать мне, что DTO означает Por пользу ? –

+0

Объект передачи данных - см. Http://en.wikipedia.org/wiki/Data_transfer_object –

+0

По существу объектная модель, предназначенная для сериализации/транспорта - например, если ваша * основная * объектная модель неизменна (нет «сеттеров»), DTO может имеют свойства чтения/записи (поскольку это хорошо работает с некоторыми сериализаторами) или сплюснутой иерархией. –

Смежные вопросы