2015-01-27 13 views
6

Ich versuche, ein großes XML-Dokument (mit einem XmlReader) in einem einzigen Durchlauf und nur deserialize zu verarbeiten bestimmte Elemente in ihm mit einem XmlSerializer.Deserialisieren eines einzelnen Elements in einem großen XML-Dokument: xmlSerializer.Deserialize (xmlReader.ReadSubtree()) schlägt aufgrund von Namespace-Problemen fehl

Unten finden Sie einen Code und ein kleines XML-Dokument, das zeigt, wie ich das versucht habe.

Rationale für die Verwendung von XmlReader:1. ich mit sehr großen XML-Dokumenten zu tun habe (10 – 250 MB), die aus diesem Grund möchte ich nicht in den Speicher laden. Also XmlDocument kommt nicht in Frage. 2. Ich möchte nur bestimmte Elemente extrahieren. Normalerweise kann ich die meisten anderen Inhalte ignorieren. XmlReader scheint mir ein effizientes Mittel zum Überspringen irrelevanter Inhalte zu geben. 3. Ich weiß nicht im Voraus, ob irgendwelche und alle Elemente, mit denen ich umgehen kann, vorhanden sein werden; Daher verwende ich nicht eine Menge von /XQuery oder LINQ zu XML-basierten Abfragen, weil ich nur einen einzigen Durchlauf über die XML-Dateien (aufgrund ihrer Größe) machen möchte.

public class ElementOfInterest { } 
… 

var xml = @"<?xml version='1.0' encoding='utf-8' ?> 
      <Root xmlns:ex='urn:stakx:example' 
        xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance'> 
       <ElementOfInterest xsi:type='ex:ElementOfInterest' /> 
      </Root>"; 

var reader = System.Xml.XmlReader.Create(new System.IO.StringReader(xml)); 
reader.ReadToFollowing("ElementOfInterest"); 

var serializer = new System.Xml.Serialization.XmlSerializer(typeof(ElementOfInterest)); 
serializer.Deserialize(reader.ReadSubtree()); 

Die letzte Zeile des Codes führt die folgende innere Ausnahme:

InvalidOperationException: "Namespacepräfix ex ist nicht definiert"

Offensichtlich ist, dass die XmlSerializer nicht den Wert des ex Namespacepräfix innerhalb des xsi:type Attribut erkennen.

Dies ist nur ein Fehler, den ich habe, aber ehrlich gesagt, das größere Problem ist, dass ich keine Ahnung habe, wie man über das ganze Namespace-Problem geht. Ich bin einfach auf der Suche nach einer bequemen Möglichkeit, nur einen einzigen Knoten aus dem XML-Dokument zu deserialisieren, aber das scheint zu bedeuten, Namespaces manuell registrieren/verwalten zu müssen und sie irgendwie von der XmlReader an die XmlSerializer weiterzuleiten.

Kann jemand demonstrieren, wie ein einzelner Knoten aus einem XML-Dokument mit einem XmlReader deserialisiert wird, indem entweder auf den Fehler in meinem Code hingewiesen wird oder indem ein alternativer Ansatz gezeigt wird?

+3

Suchen Sie ein Beispiel für 'XmlNamespaceManager'. Hier ist [one] (http://stackoverflow.com/a/14462578/815938) zu starten. – kennyzx

+0

@kennyzx: Ich habe 'XmlNamespaceManager' und' XmlNameTable' und 'XmlParserContext' angeschaut, und was nicht. Ich habe einfach keine Ahnung, wie es in meinem Szenario zusammenpassen soll. Könntest du bitte seinen Nutzen für mich demonstrieren? – stakx

Antwort

5

Die folgenden Werke:

using System.IO; 
using System.Xml; 
using System.Xml.Serialization; 

static void Main() 
{ 
    var xml = @"<?xml version='1.0' encoding='utf-8' ?> 
       <Root 
        xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance' 
        xmlns:ex='urn:stakx:example' 
       > 
        <ex:ElementOfInterest xsi:type='ex:ElementOfInterest' /> 
       </Root>"; 

    var nt = new NameTable(); 
    var mgr = new XmlNamespaceManager(nt); 
    mgr.AddNamespace("ex", "urn:stakx:example"); 
    var ctxt = new XmlParserContext(nt, mgr, "", XmlSpace.Default); 
    var reader = XmlReader.Create(new StringReader(xml), null, ctxt); 
    var serializer = new XmlSerializer(typeof(ElementOfInterest)); 

    reader.ReadToFollowing("ElementOfInterest", "urn:stakx:example"); 
    var eoi = (ElementOfInterest)serializer.Deserialize(reader.ReadSubtree()); 
} 

[XmlRoot(Namespace = "urn:stakx:example")] 
public class ElementOfInterest { } 

Beachten Sie den Namensraum im Eingang: <ex:ElementOfInterest>.

+0

Können Sie erklären, warum Sie das Eingabedokument geändert haben (z. B. das Namespacepräfix dem Element hinzugefügt haben)? Ist es, um Ihren Code arbeiten zu lassen, oder weil * meine * Version der Beispieleingabe in irgendeiner Weise nicht wohlgeformt/ungültig war? – stakx

+1

Beide, irgendwie.Nun, Ihr Eingabedokument gibt an, dass das resultierende Objekt im Namensraum "urn: stakx: example" sein sollte. Die Zielklasse 'ElementOfInterest' hat dies nicht berücksichtigt. Daher war das Hinzufügen des Klassenattributs' XmlRoot (Namespace = ...) 'die erste Änderung. Wenn Sie nun ein Objekt "ElementOfInterest" erneut serialisieren, befindet sich das resultierende XML-Element ebenfalls im Namensraum "urn: stakx: example". Um Deserialisierung und Serialisierung symmetrisch zu machen, musste ich das Element in den Namespace der Eingabe einfügen. – Tomalak