2009-03-24 11 views
48

Sto scrivendo uno strumento GIS client in C# per recuperare "funzioni" in uno schema XML basato su GML (esempio sotto) da un server. Gli estratti sono limitati a 100.000 funzioni.Qual è il modo migliore per analizzare (grande) XML nel codice C#?

ho stima ipotetica che la più grande extract.xml potrebbe arrivare fino a circa 150 megabyte, quindi ovviamente parser DOM sono fuori ho cercato di decidere tra XmlSerializer e XSD.EXE attacchi generati --OPPURE-- XmlReader e azionati a mano grafico dell'oggetto creato.

O forse c'è un modo migliore che non ho ancora considerato? Come XLINQ o ????

Per favore qualcuno può guidarmi? Soprattutto per quanto riguarda l'efficienza della memoria di un determinato approccio. Altrimenti dovrò "prototipizzare" entrambe le soluzioni e profilarle fianco a fianco.

Sono un po 'un gambero crudo in. NET. Qualsiasi suggerimento sarebbe davvero apprezzato.

Grazie. Keith.


XML di esempio - fino a 100.000 di loro, fino a 234,600 coordinate per funzionalità.

<feature featId="27168306" fType="vegetation" fTypeId="1129" fClass="vegetation" gType="Polygon" ID="0" cLockNr="51598" metadataId="51599" mdFileId="NRM/TIS/VEGETATION/9543_22_v3" dataScale="25000"> 
    <MultiGeometry> 
    <geometryMember> 
     <Polygon> 
     <outerBoundaryIs> 
      <LinearRing> 
      <coordinates>153.505004,-27.42196 153.505044,-27.422015 153.503992 .... 172 coordinates omitted to save space ... 153.505004,-27.42196</coordinates> 
      </LinearRing> 
     </outerBoundaryIs> 
     </Polygon> 
    </geometryMember> 
    </MultiGeometry> 
</feature> 
+0

una risposta un po 'in ritardo: SAX/L'analisi dello streaming Stax è limitata nell'utilità, prova vtd-xml, che usa 1/5 di DOM –

risposta

53

Utilizzare XmlReader per analizzare documenti XML di grandi dimensioni. XmlReader fornisce accesso rapido, solo diretto, senza cache ai dati XML. (Inoltra solo significa che puoi leggere il file XML dall'inizio alla fine ma non puoi tornare indietro nel file.) XmlReader utilizza piccole quantità di memoria ed è equivalente all'utilizzo di un semplice lettore SAX.

using (XmlReader myReader = XmlReader.Create(@"c:\data\coords.xml")) 
    { 
     while (myReader.Read()) 
     { 
      // Process each node (myReader.Value) here 
      // ... 
     } 
    } 

È possibile utilizzare XmlReader per elaborare file con dimensioni massime di 2 gigabyte (GB).

Rif: How to read XML from a file by using Visual C#

+4

IIRC, .NET 2.0 in poi, MS raccomanda di utilizzare la classe XmlReader direttamente anziché XmlTextReader. – Cerebrus

+0

@Cerebrus e Mitch: Grazie signori. Questo è più o meno quello che pensavo, ma è davvero molto bello avere una seconda opinione (informata) prima di sprecare giorni a perseguire potenzialmente la strada sbagliata. Molto apprezzato! – corlettk

+2

In CF si usa anche il censore speciale. XmlReader.Create poichè è ottimizzato un po 'e ofc ricorda di utilizzare .Skip() per saltare gli elementi che non sono interessanti! – Quibblesome

6

Un SAX parser potrebbe essere quello che stai cercando. SAX non richiede di leggere l'intero documento in memoria - lo analizza in modo incrementale e consente di elaborare gli elementi man mano che si procede. Non so se c'è un parser SAX fornite in .NET, ma ci sono alcune opzioni opensource che si poteva guardare:

Ecco un post correlati :

+1

Sarebbe interessante confrontare le prestazioni di Sax v XmlTextReader - qualcuno ha provato questo – MrTelly

+0

Sarei interessato anch'io, non li ho confrontati –

+0

.NET non fornisce un parser nativo per sax, ma ho letto un articolo (in slashdot, penso) che mostrava quanto fosse facile girare il proprio parser SAX usando i "primitivi" di XmlReader. – corlettk

12

Solo per riassumere e rendere la risposta un po 'più ovvia per chiunque trovi questo thread in google.

Prima NET 2 XmlTextReader è stato il più efficiente della memoria parser XML disponibile nella API standard (grazie Mitch ;-)

NET 2 ha introdotto la classe XmlReader che è meglio di nuovo E 'un elemento forward-only iteratore (un po 'come un parser StAX).(grazie Cerebrus ;-)

E ricorda kiddies, di qualsiasi istanza XML ha il potenziale per essere più grande di circa 500k, NON USARE DOM!

Saluti tutti. Keith.

15

Asat 14 maggio 2009: Sono passato all'utilizzo di un approccio ibrido ... vedere il codice di seguito.

Questa versione ha la maggior parte dei vantaggi di entrambi:
    * XmlReader/XmlTextReader (efficienza della memoria -> velocità); e
    * XmlSerializer (code-gen -> sviluppo expediancy e flessibilità).

Utilizza XmlTextReader per scorrere il documento e crea "doclet" che deserializza utilizzando le classi XmlSerializer e "XML binding" generate con XSD.EXE.

Immagino che questa ricetta sia universalmente applicabile, ed è veloce ... Sto analizzando un documento XML da 201 MB contenente 56.000 funzionalità GML in circa 7 secondi ... la vecchia implementazione VB6 di questa applicazione ha richiesto alcuni minuti (o addirittura ore) per analizzare grandi estratti ... quindi sto cercando di andare.

Ancora una volta, un BIG Grazie ai forum per donare il tuo tempo prezioso. Lo apprezzo molto.

Saluti tutti. Keith.

using System; 
using System.Reflection; 
using System.Xml; 
using System.Xml.Serialization; 
using System.IO; 
using System.Collections.Generic; 

using nrw_rime_extract.utils; 
using nrw_rime_extract.xml.generated_bindings; 

namespace nrw_rime_extract.xml 
{ 
    internal interface ExtractXmlReader 
    { 
     rimeType read(string xmlFilename); 
    } 

    /// <summary> 
    /// RimeExtractXml provides bindings to the RIME Extract XML as defined by 
    /// $/Release 2.7/Documentation/Technical/SCHEMA and DTDs/nrw-rime-extract.xsd 
    /// </summary> 
    internal class ExtractXmlReader_XmlSerializerImpl : ExtractXmlReader 
    { 
     private Log log = Log.getInstance(); 

     public rimeType read(string xmlFilename) 
     { 
      log.write(
       string.Format(
        "DEBUG: ExtractXmlReader_XmlSerializerImpl.read({0})", 
        xmlFilename)); 
      using (Stream stream = new FileStream(xmlFilename, FileMode.Open)) 
      { 
       return read(stream); 
      } 
     } 

     internal rimeType read(Stream xmlInputStream) 
     { 
      // create an instance of the XmlSerializer class, 
      // specifying the type of object to be deserialized. 
      XmlSerializer serializer = new XmlSerializer(typeof(rimeType)); 
      serializer.UnknownNode += new XmlNodeEventHandler(handleUnknownNode); 
      serializer.UnknownAttribute += 
       new XmlAttributeEventHandler(handleUnknownAttribute); 
      // use the Deserialize method to restore the object's state 
      // with data from the XML document. 
      return (rimeType)serializer.Deserialize(xmlInputStream); 
     } 

     protected void handleUnknownNode(object sender, XmlNodeEventArgs e) 
     { 
      log.write(
       string.Format(
        "XML_ERROR: Unknown Node at line {0} position {1} : {2}\t{3}", 
        e.LineNumber, e.LinePosition, e.Name, e.Text)); 
     } 

     protected void handleUnknownAttribute(object sender, XmlAttributeEventArgs e) 
     { 
      log.write(
       string.Format(
        "XML_ERROR: Unknown Attribute at line {0} position {1} : {2}='{3}'", 
        e.LineNumber, e.LinePosition, e.Attr.Name, e.Attr.Value)); 
     } 

    } 

    /// <summary> 
    /// xtractXmlReader provides bindings to the extract.xml 
    /// returned by the RIME server; as defined by: 
    /// $/Release X/Documentation/Technical/SCHEMA and 
    /// DTDs/nrw-rime-extract.xsd 
    /// </summary> 
    internal class ExtractXmlReader_XmlTextReaderXmlSerializerHybridImpl : 
     ExtractXmlReader 
    { 
     private Log log = Log.getInstance(); 

     public rimeType read(string xmlFilename) 
     { 
      log.write(
       string.Format(
        "DEBUG: ExtractXmlReader_XmlTextReaderXmlSerializerHybridImpl." + 
        "read({0})", 
        xmlFilename)); 

      using (XmlReader reader = XmlReader.Create(xmlFilename)) 
      { 
       return read(reader); 
      } 

     } 

     public rimeType read(XmlReader reader) 
     { 
      rimeType result = new rimeType(); 
      // a deserializer for featureClass, feature, etc, "doclets" 
      Dictionary<Type, XmlSerializer> serializers = 
       new Dictionary<Type, XmlSerializer>(); 
      serializers.Add(typeof(featureClassType), 
       newSerializer(typeof(featureClassType))); 
      serializers.Add(typeof(featureType), 
       newSerializer(typeof(featureType))); 

      List<featureClassType> featureClasses = new List<featureClassType>(); 
      List<featureType> features = new List<featureType>(); 
      while (!reader.EOF) 
      { 
       if (reader.MoveToContent() != XmlNodeType.Element) 
       { 
        reader.Read(); // skip non-element-nodes and unknown-elements. 
        continue; 
       } 

       // skip junk nodes. 
       if (reader.Name.Equals("featureClass")) 
       { 
        using (
         StringReader elementReader = 
          new StringReader(reader.ReadOuterXml())) 
        { 
         XmlSerializer deserializer = 
          serializers[typeof (featureClassType)]; 
         featureClasses.Add(
          (featureClassType) 
          deserializer.Deserialize(elementReader)); 
        } 
        continue; 
        // ReadOuterXml advances the reader, so don't read again. 
       } 

       if (reader.Name.Equals("feature")) 
       { 
        using (
         StringReader elementReader = 
          new StringReader(reader.ReadOuterXml())) 
        { 
         XmlSerializer deserializer = 
          serializers[typeof (featureType)]; 
         features.Add(
          (featureType) 
          deserializer.Deserialize(elementReader)); 
        } 
        continue; 
        // ReadOuterXml advances the reader, so don't read again. 
       } 

       log.write(
        "WARNING: unknown element '" + reader.Name + 
        "' was skipped during parsing."); 
       reader.Read(); // skip non-element-nodes and unknown-elements. 
      } 
      result.featureClasses = featureClasses.ToArray(); 
      result.features = features.ToArray(); 
      return result; 
     } 

     private XmlSerializer newSerializer(Type elementType) 
     { 
      XmlSerializer serializer = new XmlSerializer(elementType); 
      serializer.UnknownNode += new XmlNodeEventHandler(handleUnknownNode); 
      serializer.UnknownAttribute += 
       new XmlAttributeEventHandler(handleUnknownAttribute); 
      return serializer; 
     } 

     protected void handleUnknownNode(object sender, XmlNodeEventArgs e) 
     { 
      log.write(
       string.Format(
        "XML_ERROR: Unknown Node at line {0} position {1} : {2}\t{3}", 
        e.LineNumber, e.LinePosition, e.Name, e.Text)); 
     } 

     protected void handleUnknownAttribute(object sender, XmlAttributeEventArgs e) 
     { 
      log.write(
       string.Format(
        "XML_ERROR: Unknown Attribute at line {0} position {1} : {2}='{3}'", 
        e.LineNumber, e.LinePosition, e.Attr.Name, e.Attr.Value)); 
     } 
    } 
} 
1

Volevo solo aggiungere questo metodo di estensione semplice come un esempio di utilizzo XmlReader (come rispose Mitch):

public static bool SkipToElement (this XmlReader xmlReader, string elementName) 
{ 
    if (!xmlReader.Read()) 
     return false; 

    while (!xmlReader.EOF) 
    { 
     if (xmlReader.NodeType == XmlNodeType.Element && xmlReader.Name == elementName) 
      return true; 

     xmlReader.Skip(); 
    } 

    return false; 
} 

e di utilizzo:

using (var xml_reader = XmlReader.Create (this.source.Url)) 
{ 
    if (!SkipToElement (xml_reader, "Root")) 
     throw new InvalidOperationException ("XML element \"Root\" was not found."); 

    if (!SkipToElement (xml_reader, "Users")) 
     throw new InvalidOperationException ("XML element \"Root/Users\" was not found."); 

    ... 
} 
+0

Nice ... Un miglioramento suggerito: l'assenza se l'elemento ricercato è sempre terminale dell'operazione corrente (deve essere saltato il nostro lettore su EOF) quindi basta lanciare l'eccezione direttamente in SkipTo invece di restituire il falso ...hai il nome dell'elemento ricercato da segnalare, quindi usalo invece di ripeterlo nei messaggi di errore. – corlettk

+0

Sì, hai ragione. È solo nel mio caso specifico che avevo bisogno di raccontare il percorso completo dell'elemento mancante e non solo il suo nome. –

Problemi correlati