Serialisierung und Deserialisierung

Windows Communication Foundation (WCF) enthält ein neues Serialisierungsmodul, den DataContractSerializer. Der DataContractSerializer übersetzt in beiden Richtungen zwischen .NET Framework-Objekten und XML. In diesem Thema wird die Funktionsweise des Serialisierungsprogramms erklärt.

Beim Serialisieren von .NET Framework-Objekten erkennt das Serialisierungsmodul verschiedene Serialisierungsprogrammiermodelle, einschließlich des neuen Datenvertragsmodells. Eine vollständige Liste der unterstützten Typen finden Sie unter Types Supported by the Data Contract Serializer. Eine Einführung in Datenverträge finden Sie unter Using Data Contracts.

Beim Deserialisieren von XML verwendet das Serialisierungsprogramm die XmlReader -Klasse und die XmlWriter -Klasse. Zudem werden die Klassen XmlDictionaryReader und XmlDictionaryWriter unterstützt, damit in bestimmten Fällen optimierter XML-Code erzeugt werden kann, beispielsweise bei der Verwendung des binären XML-Formats von WCF.

WCF enthält auch ein zugehöriges Serialisierungsmodul namens NetDataContractSerializer. Die NetDataContractSerializer:

  • Ist nicht sicher. Weitere Informationen finden Sie im Sicherheitsleitfaden für BinaryFormatter.
  • Ähnelt den Serialisierungsmodulen BinaryFormatter und SoapFormatter, da auch hier .NET Framework-Typnamen zusammen mit den serialisierten Daten ausgegeben werden.
  • Wird verwendet, wenn die Endpunkte für die Serialisierung und die Deserialisierung die gleichen Datentypen verwenden.

Sowohl DataContractSerializer als auch NetDataContractSerializer werden von der gemeinsamen Basisklasse XmlObjectSerializer abgeleitet.

Warnung

Der DataContractSerializer serialisiert Zeichenfolgen mit Steuerzeichen mit einem Hexadezimalwert kleiner als 20 als XML-Entitäten. Dies kann bei einem Nicht-WCF-Client zu einem Problem führen, wenn solche Daten an einen WCF-Dienst gesendet werden.

Erstellen einer DataContractSerializer-Instanz

Das Erstellen einer DataContractSerializer -Instanz ist ein wichtiger Schritt. Sie können nach der Erstellung dieser Instanz keine Einstellungen mehr ändern.

Angeben des Stammtyps

Der Stammtyp ist der Typ, von dem Instanzen serialisiert oder deserialisiert werden. Für DataContractSerializer sind viele überladene Konstruktoren verfügbar; aber mindestens ein Stammtyp muss mit dem Parameter type angegeben werden.

Ein für einen bestimmten Stammtyp erstelltes Serialisierungsprogramm kann nur dann zum Serialisieren (oder Deserialisieren) eines anderen Typs verwendet werden, wenn dieser Typ vom Stammtyp abgeleitet ist. Das folgende Beispiel zeigt zwei Klassen.

[DataContract]
public class Person
{
    // Code not shown.
}

[DataContract]
public class PurchaseOrder
{
    // Code not shown.
}
<DataContract()> _
Public Class Person
    ' Code not shown.
End Class

<DataContract()> _
Public Class PurchaseOrder
    ' Code not shown.
End Class

In diesem Code wird eine Instanz der DataContractSerializer -Klasse erstellt, die lediglich zum Serialisieren und Deserialisieren der Person -Klasse verwendet werden kann.

DataContractSerializer dcs = new DataContractSerializer(typeof(Person));
// This can now be used to serialize/deserialize Person but not PurchaseOrder.
Dim dcs As New DataContractSerializer(GetType(Person))
' This can now be used to serialize/deserialize Person but not PurchaseOrder.

Angeben von bekannten Typen

Wenn die serialisierten Typen polymorph sind und die Polymorphie nicht bereits mithilfe des KnownTypeAttribute -Attributs oder eines anderen Mechanismus gehandhabt wird, dann muss dem Konstruktor der Serializer-Instanz im knownTypes -Parameter eine Liste der möglichen bekannten Typen übergeben werden. Weitere Informationen zu bekannten Typen finden Sie unter Bekannte Typen in Datenverträgen.

Im folgenden Codebeispiel wird die Klasse namens LibraryPatronveranschaulicht, die eine Auflistung des Typs LibraryItementhält. Die zweite Klasse definiert den LibraryItem -Typ. Die dritte Klasse und die vierte Klasse (Book und Newspaper) sind von der LibraryItem -Klasse abgeleitet.

[DataContract]
public class LibraryPatron
{
    [DataMember]
    public LibraryItem[] borrowedItems;
}
[DataContract]
public class LibraryItem
{
    // Code not shown.
}

[DataContract]
public class Book : LibraryItem
{
    // Code not shown.
}

[DataContract]
public class Newspaper : LibraryItem
{
    // Code not shown.
}
<DataContract()> _
Public Class LibraryPatron
    <DataMember()> _
    Public borrowedItems() As LibraryItem
End Class

<DataContract()> _
Public Class LibraryItem
    ' Code not shown.
End Class

<DataContract()> _
Public Class Book
    Inherits LibraryItem
    ' Code not shown.
End Class

<DataContract()> _
Public Class Newspaper
    Inherits LibraryItem
    ' Code not shown.
End Class

Im folgenden Code wird unter Angabe des knownTypes -Parameters eine Serializer-Instanz erstellt.

// Create a serializer for the inherited types using the knownType parameter.
Type[] knownTypes = new Type[] { typeof(Book), typeof(Newspaper) };
DataContractSerializer dcs =
new DataContractSerializer(typeof(LibraryPatron), knownTypes);
// All types are known after construction.
' Create a serializer for the inherited types using the knownType parameter.
Dim knownTypes() As Type = {GetType(Book), GetType(Newspaper)}
Dim dcs As New DataContractSerializer(GetType(LibraryPatron), knownTypes)
' All types are known after construction.

Angeben von Standardstammnamen und -namespace

Wenn ein Objekt serialisiert wird, werden der Standardname und der Namespace des äußersten XML-Elements in der Regel anhand des Namens und Namespace des Datenvertrags bestimmt. Die Namen aller inneren Elemente werden aus den Datenmembernamen ermittelt, und als Namespace wird ihnen der Namespace des Datenvertrags zugeordnet. Im folgenden Beispiel werden die Werte für Name und Namespace in den Konstruktoren für die DataContractAttribute -Klasse und die DataMemberAttribute -Klasse festgelegt.

[DataContract(Name = "PersonContract", Namespace = "http://schemas.contoso.com")]
public class Person2
{
    [DataMember(Name = "AddressMember")]
    public Address theAddress;
}

[DataContract(Name = "AddressContract", Namespace = "http://schemas.contoso.com")]
public class Address
{
    [DataMember(Name = "StreetMember")]
    public string street;
}
<DataContract(Name:="PersonContract", [Namespace]:="http://schemas.contoso.com")> _
Public Class Person2
    <DataMember(Name:="AddressMember")> _
    Public theAddress As Address
End Class

<DataContract(Name:="AddressContract", [Namespace]:="http://schemas.contoso.com")> _
Public Class Address
    <DataMember(Name:="StreetMember")> _
    Public street As String
End Class

Durch die Serialisierung einer Instanz der Person -Klasse wird eine XML-Ausgabe erzeugt, die folgendem XML-Code ähnelt.

<PersonContract xmlns="http://schemas.contoso.com">  
  <AddressMember>  
    <StreetMember>123 Main Street</StreetMember>  
   </AddressMember>  
</PersonContract>  

Sie können den Standardnamen und den Namespace des Stammelements anpassen, indem Sie Werte für den rootName -Parameter und den rootNamespace -Parameter dem DataContractSerializer -Konstruktor übergeben. Beachten Sie, dass sich der rootNamespace -Parameter nicht auf den Namespace der darin enthaltenen Elemente auswirkt, die Datenmembern entsprechen. Die Parameterangabe betrifft nur den Namespace des äußersten Elements.

Diese Werte können als Zeichenfolgen oder als Instanzen der XmlDictionaryString -Klasse übergeben werden, damit sie unter Verwendung des binären XML-Formats optimiert werden können.

Festlegen des maximalen Objektkontingents

Einige DataContractSerializer -Konstruktorüberladungen verfügen über den maxItemsInObjectGraph -Parameter. Dieser Parameter legt die maximale Anzahl von Objekten fest, die das Serialisierungsprogramm in einem ReadObject -Methodenaufruf serialisieren oder deserialisieren kann. (Die Methode liest immer ein Stammobjekt, dieses Objekt kann jedoch in seinen Datenmembern andere Objekte enthalten. Diese Objekte können wiederum andere Objekte enthalten usw.) Der Standardwert ist 65536. Beachten Sie, dass beim Serialisieren und Deserialisieren von Arrays jeder Arrayeintrag als separates Objekt betrachtet wird. Beachten Sie zudem, dass einige Objekte über eine große Speicherdarstellung verfügen und dass dieses Kontingent allein daher möglicherweise nicht ausreicht, um einen Denial-of-Service-Angriff zu verhindern. Weitere Informationen finden Sie unter Sicherheitsüberlegungen zu Daten. Wenn Sie das Kontingent über die Standardeinstellung hinaus erhöhen müssen, müssen Sie dies unbedingt sowohl auf der sendenden (serialisierenden) als auch der empfangenden (deserialisierenden) Seite tun, da das Kontingent sowohl für das Lesen als auch das Schreiben von Daten gilt.

Roundtrips

Ein Roundtrip tritt auf, wenn ein Objekt in einem Vorgang deserialisiert und erneut serialisiert wird. Das heißt, Daten werden aus XML in eine Objektinstanz und wieder zurück in einen XML-Stream umgewandelt.

Einige DataContractSerializer -Konstruktorüberladungen verfügen über den ignoreExtensionDataObject -Parameter, der standardmäßig auf false festgelegt ist. In diesem Standardmodus können Daten von einer neueren Version des Datenvertrags zu einer älteren Datenvertragsversion und wieder zurück zur neueren Version geschickt werden, ohne dass Datenverluste auftreten, sofern der Datenvertrag die IExtensibleDataObject -Schnittstelle implementiert. Nehmen wir beispielsweise an, Version 1 des Person -Datenvertrags enthält die Datenmember Name und PhoneNumber , und in Version 2 wurde der Member Nickname hinzugefügt. Wenn IExtensibleDataObject implementiert wurde, dann werden beim Senden der Daten von Version 2 an Version 1 die Nickname -Daten gespeichert und beim erneuten Serialisieren der Daten wieder ausgegeben. Daher gehen in Roundtrips keine Daten verloren. Weitere Informationen finden Sie unter Aufwärtskompatible Datenverträge und Datenvertragsversionsverwaltung.

Bedenken hinsichtlich der Sicherheit und Schemavalidierung bei Roundtrips

Roundtrips beeinträchtigen möglicherweise die Sicherheit. Beispielsweise kann das Deserialisieren und Speichern von großen Mengen externer Daten ein Sicherheitsrisiko darstellen. Es kann Sicherheitsbedenken in Bezug auf die erneute Ausgabe dieser Daten geben, weil sich diese nicht verifizieren lassen, insbesondere wenn sie digital signiert sind. Beispielsweise könnte im obigen Szenario der Endpunkt mit Version 1 einen Nickname -Wert signieren, der bösartige Daten enthält. Schließlich kann es Bedenken hinsichtlich der Schemavalidierung geben: Es kann wünschenswert sein, dass ein Endpunkt nur Daten, die genau dem angegebenen Datenvertrag entsprechen, und keine zusätzlichen Werte ausgibt. Im vorigen Beispiel besagt der Datenvertrag des Endpunkts mit Version 1, dass dieser nur Name und PhoneNumberausgibt, und bei Verwendung einer Schemavalidierung verursacht die Ausgabe des zusätzlichen Nickname -Werts einen Fehler.

Aktivieren und Deaktivieren von Roundtrips

Wenn Sie Roundtrips deaktivieren möchten, implementieren Sie die IExtensibleDataObject -Schnittstelle nicht. Falls Sie keine Kontrolle über die Typen haben, legen Sie den ignoreExtensionDataObject -Parameter auf true fest, um Roundtrips zu unterbinden.

Objektdiagrammbeibehaltung

Normalerweise befasst sich das Serialisierungsprogramm nicht mit der Objektidentität, wie im folgenden Code gezeigt.

[DataContract]
public class PurchaseOrder
{
    [DataMember]
    public Address billTo;
    [DataMember]
    public Address shipTo;
}

[DataContract]
public class Address
{
    [DataMember]
    public string street;
}
<DataContract()> _
Public Class PurchaseOrder

    <DataMember()> _
    Public billTo As Address

    <DataMember()> _
    Public shipTo As Address

End Class

<DataContract()> _
Public Class Address

    <DataMember()> _
    Public street As String

End Class

Im folgenden Code wird ein PurchaseOrder-Objekt erstellt.

// Construct a purchase order:
Address adr = new Address();
adr.street = "123 Main St.";
PurchaseOrder po = new PurchaseOrder();
po.billTo = adr;
po.shipTo = adr;
' Construct a purchase order:
Dim adr As New Address()
adr.street = "123 Main St."
Dim po As New PurchaseOrder()
po.billTo = adr
po.shipTo = adr

Beachten Sie, dass das billTo -Feld und shipTo -Feld auf die gleiche Objektinstanz festgelegt werden. Das generierte XML dupliziert allerdings die doppelt vorhandenen Informationen und ähnelt dem folgenden XML.

<PurchaseOrder>  
  <billTo><street>123 Main St.</street></billTo>  
  <shipTo><street>123 Main St.</street></shipTo>  
</PurchaseOrder>  

Dieser Ansatz verfügt über die folgenden Eigenschaften, die möglicherweise unerwünscht sind:

  • Leistung. Die Datenreplizierung ist ineffizient.

  • Zirkuläre Verweise. Wenn Objekte auf sich selbst verweisen, resultiert die Serialisierung durch Replizierung auch dann in einer Endlosschleife, wenn die Verweise über andere Objekte führen. (Das Serialisierungsprogramm löst in diesem Fall eine SerializationException aus.)

  • Semantik. Gelegentlich ist es wichtig, die Tatsache zu vermerken, dass zwei Verweise auf das gleiche Objekt und nicht auf zwei identische Objekte zeigen.

Aus diesem Grund verfügen einige DataContractSerializer -Konstruktorüberladungen über den preserveObjectReferences -Parameter (dessen Standardwert lautet false). Wenn dieser Parameter auf true festgelegt ist, wird eine spezielle Methode zur Codierung von Objektverweisen verwendet, die nur WCF versteht. Wenn der Parameter auf truefestgelegt wird, sieht das XML-Codebeispiel etwa wie folgt aus.

<PurchaseOrder ser:id="1">  
  <billTo ser:id="2"><street ser:id="3">123 Main St.</street></billTo>  
  <shipTo ser:ref="2"/>  
</PurchaseOrder>  

Der Namespace „ser“ verweist auf den Standardserialisierungsnamespace http://schemas.microsoft.com/2003/10/Serialization/. Jedes Datenelement wird nur einmal serialisiert und erhält eine ID-Nummer. Bei nachfolgender Verwendung wird ein Verweis auf die bereits serialisierten Daten erzeugt.

Wichtig

Ist sowohl das Attribut "id" als auch das Attribut "ref" im XMLElementdes Datenvertrags vorhanden, wird das Attribut "ref" berücksichtigt, und das Attribut "id" wird ignoriert.

Es ist wichtig, die Einschränkungen dieses Modus zu kennen:

  • Das XML, das der DataContractSerializer erzeugt, wenn der preserveObjectReferences -Parameter auf true festgelegt ist, kann von keiner anderen Technologie verarbeitet werden. Nur eine andere DataContractSerializer -Instanz, bei der der preserveObjectReferences -Parameter auch auf truefestgelegt ist, kann darauf zugreifen.

  • Es ist keine Metadaten- (Schema)-Unterstützung für diese Funktion verfügbar. Das Schema, das erzeugt wird, ist nur in dem Fall gültig, in dem preserveObjectReferences auf falsefestgelegt ist.

  • Diese Funktion bewirkt möglicherweise, dass Serialisierung und Deserialisierung langsamer ausgeführt werden. Obwohl die Daten nicht repliziert werden müssen, müssen in diesem Modus zusätzliche Objektvergleiche angestellt werden.

Achtung

Wenn der preserveObjectReferences -Modus aktiviert ist, muss der maxItemsInObjectGraph -Wert unbedingt auf das richtige Kontingent festgelegt werden. Aufgrund der Art und Weise, wie Arrays in diesem Modus behandelt werden, ist es für Angreifer einfach, eine kleine bösartige Nachricht zu erstellen, die in zu einem hohen Speicherverbrauch führt, der nur durch das maxItemsInObjectGraph -Kontingent begrenzt wird.

Angeben eines Datenvertrag-Ersatzzeichens

Einige DataContractSerializer -Konstruktorüberladungen verfügen über den dataContractSurrogate -Parameter, der möglicherweise auf nullfestgelegt ist. Andernfalls können Sie mit diesem Parameter ein Datenvertrag-Ersatzzeichenfestlegen, d. h. einen Typ, der die IDataContractSurrogate -Schnittstelle implementiert. Sie können dann mithilfe dieser Schnittstelle die Serialisierung und Deserialisierung anpassen. Weitere Informationen finden Sie unter Datenvertrag-Ersatzzeichen.

Serialisierung

Die folgenden Ausführungen gelten für alle von XmlObjectSerializerabgeleiteten Klassen, einschließlich der DataContractSerializer -Klasse und der NetDataContractSerializer -Klasse.

Einfache Serialisierung

Die grundlegendste Möglichkeit, ein Objekt zu serialisieren, besteht darin, es der WriteObject -Methode zu übergeben. Es gibt drei Überladungen, jeweils eine zum Schreiben in ein Stream-Objekt, ein XmlWriter-Objekt oder ein XmlDictionaryWriter-Objekt. Bei Verwendung der Stream -Überladung enthält die Ausgabe XML in UTF-8-Codierung. Bei Verwendung der XmlDictionaryWriter -Überladung optimiert das Serialisierungsprogramm seine Ausgabe für binäres XML.

Bei Verwendung der WriteObject-Methode verwendet das Serialisierungsmodul den Standardnamen und -namespace für das Wrapperelement und gibt diesen zusammen mit seinem Inhalt aus (siehe den Abschnitt „Angeben von Standardstammnamen und -namespace“ weiter oben).

Im folgenden Codebeispiel wird der Schreibvorgang mit XmlDictionaryWriterveranschaulicht.

Person p = new Person();
DataContractSerializer dcs =
    new DataContractSerializer(typeof(Person));
XmlDictionaryWriter xdw =
    XmlDictionaryWriter.CreateTextWriter(someStream,Encoding.UTF8 );
dcs.WriteObject(xdw, p);
Dim p As New Person()
Dim dcs As New DataContractSerializer(GetType(Person))
Dim xdw As XmlDictionaryWriter = _
    XmlDictionaryWriter.CreateTextWriter(someStream, Encoding.UTF8)
dcs.WriteObject(xdw, p)

Damit wird XML generiert, das etwa wie folgt aussieht.

<Person>  
  <Name>Jay Hamlin</Name>  
  <Address>123 Main St.</Address>  
</Person>  

Schrittweise Serialisierung

Verwenden Sie die Methoden WriteStartObject, WriteObjectContentund WriteEndObject , um das Endelement und den Inhalt des Objekts zu schreiben bzw. das Wrapperelement zu schließen.

Hinweis

Es sind keine Stream -Überladungen für diese Methoden verfügbar.

Diese schrittweise Serialisierung wird üblicherweise in zwei Fällen eingesetzt. Erstens wird sie verwendet, um Inhalte wie Attribute oder Kommentare zwischen WriteStartObject und WriteObjectContent einzufügen. Dies wird im folgenden Beispiel veranschaulicht.

dcs.WriteStartObject(xdw, p);
xdw.WriteAttributeString("serializedBy", "myCode");
dcs.WriteObjectContent(xdw, p);
dcs.WriteEndObject(xdw);
dcs.WriteStartObject(xdw, p)
xdw.WriteAttributeString("serializedBy", "myCode")
dcs.WriteObjectContent(xdw, p)
dcs.WriteEndObject(xdw)

Damit wird XML generiert, das etwa wie folgt aussieht.

<Person serializedBy="myCode">  
  <Name>Jay Hamlin</Name>  
  <Address>123 Main St.</Address>  
</Person>  

Sie wird auch häufig verwendet, um die Verwendung von WriteStartObject und WriteEndObject zu vermeiden und ein eigenes, benutzerdefiniertes Wrapperelement zu schreiben (oder die Erstellung eines Wrapperelements ganz zu umgehen), wie im folgenden Code gezeigt.

xdw.WriteStartElement("MyCustomWrapper");
dcs.WriteObjectContent(xdw, p);
xdw.WriteEndElement();
xdw.WriteStartElement("MyCustomWrapper")
dcs.WriteObjectContent(xdw, p)
xdw.WriteEndElement()

Damit wird XML generiert, das etwa wie folgt aussieht.

<MyCustomWrapper>  
  <Name>Jay Hamlin</Name>  
  <Address>123 Main St.</Address>  
</MyCustomWrapper>  

Hinweis

Die Verwendung der schrittweisen Serialisierung kann in XML resultieren, das keinem gültigen Schema entspricht.

Deserialisierung

Die folgenden Ausführungen gelten für alle von XmlObjectSerializerabgeleiteten Klassen, einschließlich der DataContractSerializer -Klasse und der NetDataContractSerializer -Klasse.

Die grundlegendste Möglichkeit, ein Objekt zu deserialisieren, besteht im Aufruf einer der Überladungen der ReadObject -Methode. Es sind drei Überladungen verfügbar, jeweils eine zum Lesen mit einem XmlDictionaryReader-Objekt, einem XmlReader-Objekt oder einem Stream-Objekt. Beachten Sie, dass die Stream -Überladung einen textbasierten XmlDictionaryReader erstellt, der nicht durch Kontingente geschützt wird und nur zum Lesen vertrauenswürdiger Daten verwendet werden sollte.

Beachten Sie außerdem, dass das von der ReadObject -Methode zurückgegebene Objekt in den entsprechenden Typ umgewandelt werden muss.

Im folgenden Code wird eine Instanz der DataContractSerializer -Klasse und ein XmlDictionaryReadererzeugt, und dann eine Person -Instanz deserialisiert.

DataContractSerializer dcs = new DataContractSerializer(typeof(Person));
FileStream fs = new FileStream(path, FileMode.Open);
XmlDictionaryReader reader =
XmlDictionaryReader.CreateTextReader(fs, new XmlDictionaryReaderQuotas());

Person p = (Person)dcs.ReadObject(reader);
Dim dcs As New DataContractSerializer(GetType(Person))
Dim fs As New FileStream(path, FileMode.Open)
Dim reader As XmlDictionaryReader = _
   XmlDictionaryReader.CreateTextReader(fs, New XmlDictionaryReaderQuotas())

Dim p As Person = CType(dcs.ReadObject(reader), Person)

Positionieren Sie den XML-Reader vor dem Aufruf der ReadObject -Methode auf dem Wrapperelement oder einem Knoten, der keinen Inhalt enthält und sich vor dem Wrapperelement befindet. Sie können zu diesem Zweck die Read -Methode der XmlReader -Klasse oder der davon abgeleiteten Klasse aufrufen und den NodeTypetesten, wie im folgenden Code gezeigt.

DataContractSerializer ser = new DataContractSerializer(typeof(Person),
"Customer", @"http://www.contoso.com");
FileStream fs = new FileStream(path, FileMode.Open);
XmlDictionaryReader reader =
XmlDictionaryReader.CreateTextReader(fs, new XmlDictionaryReaderQuotas());
while (reader.Read())
{
    switch (reader.NodeType)
    {
        case XmlNodeType.Element:
            if (ser.IsStartObject(reader))
            {
                Console.WriteLine("Found the element");
                Person p = (Person)ser.ReadObject(reader);
                Console.WriteLine("{0} {1}    id:{2}",
                    p.Name , p.Address);
            }
            Console.WriteLine(reader.Name);
            break;
    }
}
Dim ser As New DataContractSerializer(GetType(Person), "Customer", "http://www.contoso.com")
Dim fs As New FileStream(path, FileMode.Open)
Dim reader As XmlDictionaryReader = XmlDictionaryReader.CreateTextReader(fs, New XmlDictionaryReaderQuotas())

While reader.Read()
    Select Case reader.NodeType
        Case XmlNodeType.Element
            If ser.IsStartObject(reader) Then
                Console.WriteLine("Found the element")
                Dim p As Person = CType(ser.ReadObject(reader), Person)
                Console.WriteLine("{0} {1}", _
                                   p.Name, p.Address)
            End If
            Console.WriteLine(reader.Name)
    End Select
End While

Beachten Sie, dass Sie Attribute dieses Wrapperelements lesen können, bevor der Reader an ReadObjectübergeben wird.

Wenn eine der einfachen ReadObject-Überladungen verwendet wird, sucht der Deserialisierer im Wrapperelement nach dem Standardnamen und -namespace (siehe Abschnitt „Angeben von Standardstammnamen und -namespace“ weiter oben) und löst eine Ausnahme aus, wenn er ein unbekanntes Element findet. Im vorstehenden Beispiel wird das Wrapperelement <Person> erwartet. Die IsStartObject -Methode wird aufgerufen, um zu überprüfen, ob der Reader auf einem Element mit dem erwarteten Namen positioniert wurde.

Es gibt eine Möglichkeit, diese Namensüberprüfung des Wrapperelements zu deaktivieren. Einige Überladungen der ReadObject -Methode akzeptieren den Booleschen Parameter verifyObjectName, der standardmäßig auf true festgelegt ist. Wenn der Parameter auf falsefestgelegt wird, werden Name und Namespace des Wrapperelements ignoriert. Dies ist beim Lesen von XML hilfreich, das mit dem oben beschriebenen schrittweisen Serialisierungsmechanismus geschrieben wurde.

Verwenden von NetDataContractSerializer

Der Hauptunterschied zwischen DataContractSerializer und NetDataContractSerializer besteht darin, dass der DataContractSerializer mit Datenvertragsnamen arbeitet, während der NetDataContractSerializer vollständige .NET Framework-Assemblynamen und -Typnamen im serialisierten XML-Code ausgibt. Dies bedeutet, dass die Endpunkte für Serialisierung und Deserialisierung genau die gleichen Datentypen verwenden müssen. Folglich muss der Mechanismus der bekannten Typen nicht in Verbindung mit NetDataContractSerializer verwendet werden, weil stets bekannt ist, welche Typen im Einzelnen deserialisiert werden müssen.

Es können jedoch verschiedene Probleme auftreten:

  • Sicherheit. Jeder Typ, der in dem zu deserialisierenden XML gefunden wird, wird geladen. Dies kann ausgenutzt werden, um das Laden bösartiger Typen zu erzwingen. NetDataContractSerializer sollte nur dann für nicht vertrauenswürdige Daten eingesetzt werden, wenn ein Serialisierungsbinder verwendet wird (über die Binder -Eigenschaft oder einen Konstruktorparameter). Die Binder lässt nur das Laden sicherer Typen zu. Der Bindermechanismus ist mit dem Mechanismus identisch, den die im System.Runtime.Serialization -Namespace definierten Typen verwenden.

  • Versionskontrolle. Die Angabe vollständiger Typ- und Assemblynamen im XML schränkt die Verfahren zur Versionskontrolle der Typen schwerwiegend ein. Folgendes kann nicht geändert werden: Typnamen, Namespaces, Assemblynamen und Assemblyversionen. Wenn die AssemblyFormat -Eigenschaft oder der Konstruktorparameter auf Simple statt den Standardwert Full festgelegt wird, sind Änderungen der Assemblyversion, jedoch keine generischen Parametertypen zulässig.

  • Interoperabilität. Da die XML-Ausgabe die .NET Framework-Typ- und Assemblynamen enthält, können andere Plattformen als .NET Framework nicht auf die resultierenden Daten zugreifen.

  • Leistung. Die Ausgabe von Typ- und Assemblynamen vergrößert den Umfang des resultierenden XML bedeutend.

Dieser Mechanismus ähnelt der binären Serialisierung bzw. der SOAP-Serialisierung, die von .NET Framework-Remoting verwendet wird (insbesondere von BinaryFormatter und SoapFormatter).

Die Verwendung von NetDataContractSerializer unterscheidet sich von der Verwendung von DataContractSerializerlediglich in folgender Hinsicht:

  • Die Konstruktoren erfordern keine Angabe des Stammtyps. Sie können jeden Typ mit der gleichen Instanz von NetDataContractSerializerserialisieren.

  • Die Konstruktoren akzeptieren keine Liste bekannter Typen an. Der Mechanismus der bekannten Typen ist unnötig, wenn die Typnamen in XML serialisiert werden.

  • Die Konstruktoren akzeptieren kein Datenvertrag-Ersatzzeichen. Stattdessen akzeptieren sie einen ISurrogateSelector -Parameter namens surrogateSelector (welcher der SurrogateSelector -Eigenschaft zugeordnet wird). Dies ist ein älterer Ersatzzeichenmechanismus.

  • Die Konstruktoren akzeptieren einen Parameter namens assemblyFormat vom FormatterAssemblyStyle , welcher der AssemblyFormat -Eigenschaft zugeordnet wird. Wie oben erläutert, lassen sich damit die Versionskontrollfähigkeiten des Serialisierungsprogramms verbessern. Dies entspricht dem FormatterAssemblyStyle -Mechanismus in der binären Serialisierung oder der SOAP-Serialisierung.

  • Die Konstruktoren akzeptieren einen StreamingContext -Parameter namens context , welcher der Context -Eigenschaft zugeordnet wird. Sie können diesen zur Übergabe von Informationen an Typen nutzen, die serialisiert werden. Diese Art der Verwendung entspricht dem StreamingContext -Mechanismus, der in anderen System.Runtime.Serialization -Klassen verwendet wird.

  • Die Serialize -Methode und die Deserialize -Methode sind Aliase für die WriteObject -Methode bzw. die ReadObject -Methode. Sie sollen ein konsistenteres Programmiermodell für die binäre Serialisierung oder SOAP-Serialisierung bereitstellen.

Weitere Informationen zu diesen Funktionen finden Sie unter Binäre Serialisierung.

Die von der NetDataContractSerializer -Klasse und der DataContractSerializer -Klasse verwendeten XML-Formate sind normalerweise nicht kompatibel. Das heißt, es ist nicht möglich, Daten mit einem dieser Serialisierungsprogramme zu serialisieren und sie mit dem anderen Serialisierungsprogramm zu deserialisieren.

Beachten Sie außerdem, dass der NetDataContractSerializer nicht den vollständigen .NET Framework-Typ- und Assemblynamen für jeden Knoten im Objektgraphen ausgibt. Diese Informationen werden nur im Fall möglicher Mehrdeutigkeiten ausgegeben. Das heißt, sie werden auf der Stammobjektebene und für polymorphe Fälle ausgegeben.

Siehe auch