2010-01-23 10 views
15

Sembra che ogni volta che utilizzo un XMLReader, finisco con un sacco di tentativi ed errori cercando di capire cosa sto per leggere rispetto a quello che sto leggendo rispetto a ciò che ho appena letto. Alla fine lo capisco sempre, ma, dopo averlo usato numerose volte, non riesco ancora a capire cosa sta facendo un XMLReader quando chiamo le varie funzioni. Ad esempio, quando chiamo la lettura la prima volta, se legge un tag di inizio elemento, è ora alla fine del tag dell'elemento o è pronto per iniziare a leggere gli attributi dell'elemento? Conosce GetAttribute per conoscere i valori degli attributi? Cosa succederà se chiamo ReadStartElement a questo punto? Finirà di leggere l'elemento iniziale, o cercherà quello successivo, saltando tutti gli attributi? Cosa succede se voglio leggere un numero di elementi: qual è il modo migliore per provare a leggere l'elemento successivo e determinare quale sia il suo nome. Leggi letto seguito da IsStartElement o IsStartElement restituirà informazioni sul nodo in seguito all'elemento che ho appena letto?Non riesco mai a prevedere il comportamento di XMLReader. Qualche consiglio su come capire?

Come potete vedere, mi manca davvero una comprensione di dove si trova un XMLReader durante le varie fasi della sua lettura e di come il suo stato sia influenzato da varie funzioni di lettura. C'è qualche schema semplice che ho semplicemente omesso di notare?

Ecco un altro esempio del problema (ricavate dalle risposte):

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

Primo problema, il nodo macchina è saltata completamente. MoveToContent sembra spostarsi sul contenuto dell'elemento macchina causando che non venga mai analizzato. Inoltre, se si ignora MoveToContent, viene visualizzato un errore: "'Elemento' è un XmlNodeType non valido." provando a ReadElementString, che non riesco a spiegare.

Il problema successivo è che, leggendo il primo elemento parte, ReadElementString sembra posizionare il lettore all'inizio dell'elemento parte successivo dopo la lettura. Ciò provoca il lettore. Leggi all'inizio del ciclo successivo per saltare il successivo elemento della parte saltando fino all'ultima parte. Così il risultato finale di questo codice è: Codice

Parte 01a: Braccio Destro

Parte Codice 03: Big Toe

Questo è un primo esempio del comportamento di confusign XMLReader che sto cercando di capire.

risposta

3

La mia ultima soluzione (che funziona per il mio caso corrente) è di attenersi a Read(), IsStartElement (nome) e GetAttribute (nome) nell'implementazione di una macchina a stati.

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

Dopo Leggi, IsStartElement restituisce true se hai appena letto un elemento di partenza (optinally controllando il nome dell'elemento lettura), e si può accedere a tutti gli attributi di quell'elemento immediatamente. Se tutto ciò che devi leggere sono elementi e attributi, questo è abbastanza semplice.

Modifica Il nuovo esempio pubblicato nella domanda pone alcune altre sfide. Il modo corretto di leggere che XML sembra essere simile a questo:

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

devi usare ReadString invece di ReadElementString al fine di evitare di leggere l'elemento terminale e saltando in l'inizio del prossimo elemento (lasciare il seguente Leggi() salta l'elemento finale in modo che non salti sul successivo elemento di avvio). Ancora questo sembra un po 'confuso e potenzialmente inaffidabile, ma funziona per questo caso.

Dopo un po 'di riflessione, la mia opinione è che XMLReader è troppo confuso se si utilizzano metodi per leggere contenuti diversi dal metodo di lettura. Penso che sia molto più semplice se ci si limita al metodo Read per leggere dallo stream XML. Ecco come sarebbe lavorare con il nuovo esempio (ancora una volta, a quanto pare IsStartElement, GetAttribute e Read sono i metodi principali, e si finisce con una macchina a stati):

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 per stick con Read() –

5

Ecco la cosa ... Ho scritto una buona quantità di codice di serializzazione (incluso un sacco di elaborazione xml), e mi trovo nello esattamente la la stessa barca che voi. Ho una guida molto semplice, quindi: non.

sarò felicemente uso XmlWriter come un modo per scrivere xml rapidamente, ma mi piacerebbe camminare sui carboni ardenti prima di scegliere di implementare IXmlSerializable un'altra volta - mi piacerebbe semplicemente scrivere un separato DTO e mappare i dati in questo; significa anche che lo schema (per "mex", "wsdl", ecc.) è gratuito.

+0

Potete dirmi cosa DTO significa por favor ? –

+0

Oggetto di trasferimento dati - Vedi http://en.wikipedia.org/wiki/Data_transfer_object –

+0

Essenzialmente un modello di oggetto orientato per la serializzazione/trasporto - ad esempio se il tuo modello di oggetto * main * è immutabile (nessun "setter") il DTO potrebbe avere proprietà di lettura/scrittura (poiché funziona bene con alcuni serializzatori) o una gerarchia appiattita. –

Problemi correlati