2010-01-23 10 views
15

Parece que cada vez que uso un XMLReader, termino con un montón de intentos de prueba y ensayo tratando de descubrir lo que voy a leer contra lo que estoy leyendo y lo que acabo de leer. Al final siempre lo entiendo, pero aún así, después de usarlo en numerosas ocasiones, no parezco tener una idea clara de lo que realmente hace un XMLReader cuando llamo a varias funciones. Por ejemplo, cuando llamo a Read la primera vez, si lee una etiqueta de inicio de elemento, ¿está ahora al final de la etiqueta del elemento o lista para comenzar a leer los atributos del elemento? ¿Conoce los valores de los atributos aún si llamo a GetAttribute? ¿Qué pasará si llamo a ReadStartElement en este punto? ¿Terminará de leer el elemento de inicio, o buscará el siguiente, omitiendo todos los atributos? ¿Qué ocurre si quiero leer una serie de elementos? ¿Cuál es la mejor manera de tratar de leer el siguiente elemento y determinar su nombre? ¿Leerá seguido de IsStartElement o IsStartElement devolverá información sobre el nodo que sigue al elemento que acabo de leer?Nunca puedo predecir el comportamiento de XMLReader. ¿Algún consejo sobre la comprensión?

Como puedes ver, realmente no entiendo dónde está un XMLReader durante las diversas fases de su lectura y cómo su estado se ve afectado por varias funciones de lectura. ¿Hay algún patrón simple que simplemente no he notado?

Aquí hay otro ejemplo del problema (tomado de las respuestas):

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

El primer problema, el nodo de la máquina se omite por completo. MoveToContent parece moverse al contenido del elemento de la máquina haciendo que nunca se analice. Además, si omite MoveToContent, obtendrá un error: "'Element' es un XmlNodeType no válido." intentando ReadElementString, que no puedo explicar del todo.

El siguiente problema es que, mientras se lee el elemento de la primera parte, ReadElementString parece posicionar el lector al principio del elemento de la parte siguiente después de la lectura. Esto hace que el lector lea. Al comienzo del siguiente ciclo, omita el siguiente elemento de la parte que salta directamente al último elemento de la parte. Por lo que el resultado final de este código es: Código

parte 01a: Brazo derecho

Parte código 03: Dedo gordo

Este es un buen ejemplo del comportamiento de confusign XMLReader que estoy tratando de entender.

Respuesta

3

Mi última solución (que funciona para mi caso actual) es seguir con Read(), IsStartElement (nombre) y GetAttribute (nombre) al implementar una máquina de estado.

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

Después de Leer, IsStartElement devolverá cierto si usted acaba de leer un elemento de partida (comprobación optinally el nombre del elemento de leer), y se puede acceder a todos los atributos de ese elemento de inmediato. Si todo lo que necesita leer son elementos y atributos, esto es bastante sencillo.

Editar El nuevo ejemplo publicado en la pregunta plantea algunos otros desafíos. La forma correcta de leer ese XML parece ser así:

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

usted tiene que utilizar ReadString en lugar de ReadElementString con el fin de evitar leer el elemento final y saltar al comienzo del siguiente elemento (dejar que la siguiente Leer() omita el elemento final para que no salte el siguiente elemento de inicio). Aún así, esto parece algo confuso y potencialmente poco confiable, pero funciona para este caso.

Después de pensar un poco más, mi opinión es que XMLReader es demasiado confuso si usa algún método para leer contenido que no sea el método Read. Creo que es mucho más simple si te limitas al método Read para leer de la secuencia XML. Así es como funcionaría con el nuevo ejemplo (una vez más, parece IsStartElement, GetAttribute y Lee son los métodos principales, y que terminan con una máquina de estados):

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 para pegar con Read() –

5

Esto es lo que ... He escrito una buena cantidad de código de serialización (incluido un montón de procesamiento xml), y me encuentro en exactamente el mismo barco que usted. Tengo una guía muy simple, por lo tanto: no.

voy utilizan alegremente XmlWriter como una manera de escribir XML de forma rápida, pero me gustaría caminar sobre las brasas antes de elegir para poner en práctica IXmlSerializable otra vez - Yo simplemente iba a escribir una por separado DTO y asignar los datos en eso; también significa que el esquema (para "mex", "wsdl", etc.) es gratis.

+0

¿Me puede decir lo DTO significa por favor ? –

+0

Objeto de transferencia de datos: consulte http://en.wikipedia.org/wiki/Data_transfer_object –

+0

Esencialmente un modelo de objetos orientado a la serialización/transporte; por ejemplo, si su modelo de objetos * main * es inmutable (no "establecedores"), el DTO podría tiene propiedades de lectura/escritura (ya que funciona bien con algunos serializadores), o una jerarquía plana. –

Cuestiones relacionadas