Udostępnij za pośrednictwem


Serializacja i deserializacja

Program Windows Communication Foundation (WCF) zawiera nowy silnik serializacji, DataContractSerializer. DataContractSerializer konwertuje między obiektami .NET Framework a XML w obie strony. W tym temacie wyjaśniono, jak działa serializator.

Podczas serializacji obiektów .NET Framework, serializator rozumie różne modele programowania serializacji, w tym nowy model kontraktu danych . Aby uzyskać pełną listę obsługiwanych typów, zobacz Types Supported by the Data Contract Serializer. Aby zapoznać się z wprowadzeniem do kontraktów danych, zobacz Using Data Contracts.

Podczas deserializacji xml serializator używa klas XmlReader i XmlWriter. Obsługuje również klasy XmlDictionaryReader i XmlDictionaryWriter, aby umożliwić tworzenie zoptymalizowanego kodu XML w niektórych przypadkach, na przykład w przypadku korzystania z formatu XML binarnego WCF.

WCF zawiera również serializator towarzyszący, NetDataContractSerializer. Pomocnik NetDataContractSerializer:

  • Czy nie jest bezpieczne? Aby uzyskać więcej informacji, zobacz Przewodnik po zabezpieczeniach BinaryFormatter.
  • Jest podobny do serializatorów BinaryFormatter i SoapFormatter, ponieważ emituje również nazwy typów platformy .NET Framework jako część serializowanych danych.
  • Jest używany, gdy te same typy są współdzielone podczas serializacji i deserializacji.

Zarówno DataContractSerializer, jak i NetDataContractSerializer pochodzą z wspólnej klasy bazowej, XmlObjectSerializer.

Ostrzeżenie

DataContractSerializer serializuje ciągi zawierające znaki sterujące o wartości szesnastkowej mniejszej niż 20 jako encje XML. Może to spowodować problem z klientem spoza programu WCF podczas wysyłania takich danych do usługi WCF.

Tworzenie instancji DataContractSerializer

Konstruowanie instancji DataContractSerializer jest ważnym krokiem. Po zakończeniu budowy nie można zmienić żadnego z ustawień.

Określanie typu głównego

Typ główny jest typem, którego wystąpienia są serializowane lub deserializowane. DataContractSerializer ma wiele przeciążeń konstruktorów, ale należy przynajmniej podać typ główny, korzystając z parametru type.

Serializator utworzony dla określonego typu głównego nie może służyć do serializacji (lub deserializowania) innego typu, chyba że typ pochodzi z typu głównego. W poniższym przykładzie przedstawiono dwie klasy.

[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

Ten kod tworzy wystąpienie DataContractSerializer, które może służyć tylko do serializacji lub deserializacji wystąpień klasy Person.

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.

Określanie znanych typów

Jeśli polimorfizm jest zaangażowany w typy serializowane, które nie są jeszcze obsługiwane przy użyciu atrybutu KnownTypeAttribute lub innego mechanizmu, lista możliwych znanych typów musi zostać przekazana do konstruktora serializatora przy użyciu parametru knownTypes. Aby uzyskać więcej informacji na temat znanych typów, zobacz Znane typy kontraktów danych.

W poniższym przykładzie przedstawiono klasę LibraryPatron, która zawiera kolekcję określonego typu, LibraryItem. Druga klasa definiuje typ LibraryItem. Trzecia i cztery klasy (Book i Newspaper) dziedziczą z klasy LibraryItem.

[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

Poniższy kod tworzy wystąpienie serializatora przy użyciu parametru knownTypes.

// 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.

Określanie domyślnej nazwy głównej i przestrzeni nazw

Zwykle, gdy obiekt jest serializowany, domyślna nazwa i przestrzeń nazw najbardziej zewnętrznego elementu XML są określane zgodnie z nazwą kontraktu danych i przestrzenią nazw. Nazwy wszystkich elementów wewnętrznych są określane na podstawie nazw członków danych, a ich przestrzeń nazw to przestrzeń nazw kontraktu danych. Poniższy przykład ustawia wartości Name i Namespace w konstruktorach klas DataContractAttribute i DataMemberAttribute.

[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

Serializowanie wystąpienia klasy Person generuje kod XML podobny do poniższego.

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

Można jednak dostosować domyślną nazwę i przestrzeń nazw elementu głównego, przekazując wartości parametrów rootName i rootNamespace do konstruktora DataContractSerializer. Należy pamiętać, że rootNamespace nie ma wpływu na przestrzeń nazw zawartych elementów, które odpowiadają członkom danych. Ma to wpływ tylko na przestrzeń nazwową najbardziej zewnętrznego elementu.

Te wartości można przekazać jako ciągi lub wystąpienia klasy XmlDictionaryString, aby umożliwić ich optymalizację przy użyciu binarnego formatu XML.

Ustawianie maksymalnego limitu przydziału obiektów

Niektóre przeciążenia konstruktora DataContractSerializer mają parametr maxItemsInObjectGraph. Ten parametr określa maksymalną liczbę obiektów, które serializator może serializować lub deserializować podczas jednego wywołania metody ReadObject. (Metoda zawsze odczytuje jeden obiekt główny, ale ten obiekt może mieć inne obiekty w jego składowych danych. Te obiekty mogą mieć inne obiekty itd.) Wartość domyślna to 65536. Należy pamiętać, że podczas serializacji lub deserializacji tablic każdy wpis tablicy liczy się jako oddzielny obiekt. Należy również pamiętać, że niektóre obiekty mogą mieć dużą reprezentację pamięci, dlatego sam limit przydziału może nie być wystarczający, aby zapobiec atakowi typu "odmowa usługi". Aby uzyskać więcej informacji, zobacz sekcję Zagadnienia dotyczące zabezpieczeń dla danych. Jeśli musisz zwiększyć ten przydział poza wartość domyślną, należy to zrobić zarówno po stronie wysyłania (serializacji), jak i odbierania (deserializacji), ponieważ dotyczy to zarówno odczytywania, jak i zapisywania danych.

Podróże w obie strony

, gdy obiekt jest deserializowany i ponownie serializowany w jednej operacji. W ten sposób przechodzi z XML do instancji obiektu, a następnie z powrotem do strumienia XML.

Niektóre przeciążenia konstruktora DataContractSerializer mają parametr ignoreExtensionDataObject, który jest domyślnie ustawiony na false. W tym trybie domyślnym dane mogą być wysyłane w obie strony z nowszej wersji kontraktu danych za pośrednictwem starszej wersji i z powrotem do nowszej wersji bez utraty, o ile kontrakt danych implementuje interfejs IExtensibleDataObject. Załóżmy na przykład, że wersja 1 kontraktu danych Person zawiera składowe danych Name i PhoneNumber, a wersja 2 dodaje składową Nickname. Jeśli IExtensibleDataObject jest zaimplementowane, podczas wysyłania informacji z wersji 2 do wersji 1 dane Nickname są najpierw przechowywane, a następnie ponownie wysyłane, gdy dane są ponownie serializowane; w związku z tym przy przesyłaniu danych tam i z powrotem żadne dane nie zostaną utracone. Aby uzyskać więcej informacji, zobacz Forward-Compatible Kontrakty danych i Wersjonowanie kontraktów danych.

Problemy dotyczące ważności zabezpieczeń i schematu z pełnymi cyklami

Podróże okrężne mogą mieć wpływ na bezpieczeństwo. Na przykład deserializowanie i przechowywanie dużych ilości danych nadmiarowych może stanowić zagrożenie bezpieczeństwa. Mogą wystąpić obawy dotyczące ponownego emitowania tych danych, ponieważ nie ma możliwości ich weryfikacji, zwłaszcza jeśli są zaangażowane podpisy cyfrowe. Na przykład w poprzednim scenariuszu punkt końcowy wersji 1 może podpisać wartość Nickname zawierającą złośliwe dane. Na koniec mogą występować obawy dotyczące ważności schematu: punkt końcowy może zawsze emitować dane, które ściśle przestrzegają określonego kontraktu, a nie żadnych dodatkowych wartości. W poprzednim przykładzie kontrakt punktu końcowego w wersji 1 mówi, że emituje tylko Name i PhoneNumber, a jeśli jest używana walidacja schematu, emitując dodatkową wartość Nickname powoduje niepowodzenie walidacji.

Włączanie i wyłączanie podróży w obie strony

Aby wyłączyć obiegi danych, nie implementuj interfejsu IExtensibleDataObject. Jeśli nie masz kontroli nad typami, ustaw parametr ignoreExtensionDataObject na true, aby osiągnąć ten sam efekt.

Zachowywanie grafu obiektów

Zwykle serializator nie dba o tożsamość obiektu, jak w poniższym kodzie.

[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

Poniższy kod tworzy zamówienie zakupu.

// 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

Zwróć uwagę, że pola billTo i shipTo są ustawione na to samo wystąpienie obiektu. Wygenerowany kod XML duplikuje jednak zduplikowane informacje i wygląda podobnie do poniższego kodu XML.

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

Jednak takie podejście ma następujące cechy, które mogą być niepożądane:

  • Wydajność. Replikowanie danych jest nieefektywne.

  • Odwołania cykliczne. Jeśli obiekty odwołują się do siebie, nawet za pośrednictwem innych obiektów, serializowanie przez replikację powoduje nieskończoną pętlę. (Serializator zgłasza SerializationException, jeśli tak się stanie).

  • Semantyka. Czasami ważne jest zachowanie faktu, że dwa odwołania są do tego samego obiektu, a nie do dwóch identycznych obiektów.

Z tych powodów niektóre przeciążenia konstruktora DataContractSerializer mają parametr preserveObjectReferences (wartość domyślna to false). Gdy ten parametr ma wartość true, jest używana specjalna metoda kodowania odwołań do obiektów, która jest używana tylko w programie WCF. Po ustawieniu wartości na trueprzykład kodu XML będzie teraz podobny do poniższego.

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

Przestrzeń nazw "ser" odnosi się do standardowej przestrzeni serializacji, http://schemas.microsoft.com/2003/10/Serialization/. Każda część danych jest serializowana tylko raz i otrzymuje numer identyfikacyjny, a każde późniejsze użycie odwołuje się do już serializowanych danych.

Ważne

Jeśli atrybuty "id" i "ref" są obecne w kontrakcie danych XMLElement, atrybut "ref" jest honorowany, a atrybut "id" jest ignorowany.

Ważne jest, aby zrozumieć ograniczenia tego trybu:

  • Kod XML generowany przez DataContractSerializer, gdy preserveObjectReferences jest ustawione na true, nie współdziała z żadnymi innymi technologiami i może być dostępny tylko przez inne wystąpienie DataContractSerializer, również z preserveObjectReferences ustawionym na true.

  • Brak obsługi metadanych (schematu) dla tej funkcji. Wygenerowany schemat jest prawidłowy tylko w przypadku, gdy preserveObjectReferences jest ustawiona na wartość false.

  • Ta funkcja może spowodować spowolnienie procesu serializacji i deserializacji. Chociaż dane nie muszą być replikowane, należy wykonać dodatkowe porównania obiektów w tym trybie.

Ostrożność

Po włączeniu trybu preserveObjectReferences szczególnie ważne jest ustawienie wartości maxItemsInObjectGraph na prawidłowy limit przydziału. Ze względu na sposób obsługi tablic w tym trybie, osoba atakująca może łatwo utworzyć małą złośliwą wiadomość, która powoduje duże zużycie pamięci, ograniczone jedynie przez kwotę maxItemsInObjectGraph.

Określanie zastępczego kontraktu danych

Niektóre przeciążenia konstruktora DataContractSerializer mają parametr dataContractSurrogate, który można ustawić na null. W przeciwnym razie można go użyć do określenia zastępczego kontraktu danych , który jest typem implementującym interfejs IDataContractSurrogate. Następnie można użyć interfejsu, aby dostosować proces serializacji i deserializacji. Aby uzyskać więcej informacji, zapoznaj się z Data Contract Surrogates.

Serializacja

Poniższe informacje dotyczą każdej klasy dziedziczonej z XmlObjectSerializer, w tym klas DataContractSerializer i NetDataContractSerializer.

Prosta serializacja

Najprostszym sposobem serializacji obiektu jest przekazanie go do metody WriteObject. Istnieją trzy przeciążenia, każde z nich do zapisu w Stream, XmlWriterlub XmlDictionaryWriter. W przypadku przeciążenia Stream dane wyjściowe są xml w kodowaniu UTF-8. W przypadku przeciążenia XmlDictionaryWriter serializator optymalizuje swoje dane wyjściowe do formatu XML binarnego.

W przypadku korzystania z metody WriteObject serializator używa domyślnej nazwy i przestrzeni nazw dla elementu otoki i zapisuje go wraz z zawartością (zobacz poprzednią sekcję "Określanie domyślnej nazwy głównej i przestrzeni nazw").

W poniższym przykładzie pokazano pisanie za pomocą XmlDictionaryWriter.

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)

Spowoduje to wygenerowanie kodu XML podobnego do poniższego.

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

Krok —By-Step serializacji

Użyj metod WriteStartObject, WriteObjectContenti WriteEndObject, aby zapisać element końcowy, zapisać zawartość obiektu i zamknąć odpowiednio element otoki.

Uwaga

Nie ma Stream przeciążeń tych metod.

Ta serializacja krok po kroku ma dwa typowe zastosowania. Jednym z nich jest wstawianie zawartości, takiej jak atrybuty lub komentarze między WriteStartObject i WriteObjectContent, jak pokazano w poniższym przykładzie.

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)

Spowoduje to wygenerowanie kodu XML podobnego do poniższego.

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

Innym typowym zastosowaniem jest całkowite unikanie użycia WriteStartObject i WriteEndObject oraz pisanie własnego niestandardowego elementu opakowującego (a nawet pomijanie pisania elementu opakowującego), co pokazano w poniższym kodzie.

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

Spowoduje to wygenerowanie kodu XML podobnego do poniższego.

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

Uwaga

Użycie serializacji krok po kroku może skutkować XML-em niezgodnym ze schematem.

Deserializacja

Poniższe informacje dotyczą każdej klasy dziedziczonej z XmlObjectSerializer, w tym klas DataContractSerializer i NetDataContractSerializer.

Najbardziej podstawowym sposobem deserializacji obiektu jest wywołanie jednego z przeciążeń metody ReadObject. Istnieją trzy przeciążenia, po jednym do odczytu z XmlDictionaryReader, XmlReaderlub Stream. Należy pamiętać, że przeciążenie Stream tworzy tekstową XmlDictionaryReader, która nie jest chroniona przez żadne limity, i powinna być używana tylko do odczytywania zaufanych danych.

Należy również zauważyć, że obiekt, który metoda ReadObject zwraca, musi być rzutowany do odpowiedniego typu.

Poniższy kod tworzy wystąpienie DataContractSerializer i XmlDictionaryReader, a następnie deserializuje wystąpienie Person.

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)

Przed wywołaniem metody ReadObject umieść czytnik XML w elemencie opakowania lub na węźle innym niż zawartość poprzedzającym element opakowania. Można to zrobić, wywołując metodę Read z XmlReader lub jego pochodnych oraz testując NodeType, jak pokazano w następującym kodzie.

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($"{p.Name} {p.Address}    id:{2}");
            }
            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

Należy pamiętać, że atrybuty tego elementu opakowania można odczytać przed przekazaniem interpretera do ReadObject.

Kiedy używasz jednego z prostych przeciążeń ReadObject, deserializator szuka domyślnej nazwy i przestrzeni nazw w elemencie otoki (zobacz poprzednią sekcję "Określanie domyślnej nazwy głównej i przestrzeni nazw") i zgłasza wyjątek, jeśli znajdzie nieznany element. W poprzednim przykładzie oczekiwany jest element opakowania <Person>. Metoda IsStartObject jest wywoływana w celu sprawdzenia, czy czytnik jest ustawiony na elemencie nazwanym zgodnie z oczekiwaniami.

Istnieje sposób wyłączenia kontroli nazwy elementu opakowania; niektóre przeciążenia metody ReadObject przyjmują jako parametr logiczny verifyObjectName, z ustawieniem domyślnym na true. W przypadku ustawienia wartości falsenazwa i przestrzeń nazw elementu otoki jest ignorowana. Jest to przydatne w przypadku odczytywania kodu XML napisanego przy użyciu opisanego wcześniej mechanizmu serializacji krok po kroku.

Korzystanie z narzędzia NetDataContractSerializer

Podstawową różnicą między DataContractSerializer a NetDataContractSerializer jest to, że DataContractSerializer używa nazw kontraktów danych, podczas gdy NetDataContractSerializer zwraca pełne nazwy zestawów .NET Framework i typów w serializowanym formacie XML. Oznacza to, że dokładnie te same typy muszą być współdzielone między punktami końcowymi serializacji i deserializacji. Oznacza to, że mechanizm znanych typów nie jest wymagany przy NetDataContractSerializer, ponieważ dokładne typy do deserializacji są zawsze znane.

Może jednak wystąpić kilka problemów:

  • Bezpieczeństwo. Każdy typ znaleziony w deserializowanym pliku XML zostaje załadowany. Można to wykorzystać, aby wymusić ładowanie złośliwych typów. Używanie NetDataContractSerializer z niezaufanymi danymi powinno odbywać się tylko wtedy, gdy używany jest Serialization Binder (przy użyciu właściwości Binder lub parametru konstruktora). Binder zezwala na ładowanie tylko bezpiecznych typów. Mechanizm Binder jest identyczny z tym, którego używają typy w przestrzeni nazw System.Runtime.Serialization.

  • Wersjonowanie Używanie pełnych nazw typów i zestawów w kodzie XML poważnie ogranicza sposób, w jaki typy mogą być wersjonowane. Nie można zmienić następujących elementów: nazwy typów, przestrzenie nazw, nazwy zestawów i wersje zestawów. Ustawienie właściwości AssemblyFormat lub parametru konstruktora na Simple zamiast domyślnej wartości Full umożliwia zmianę wersji zestawu, ale nie dla typów parametrów ogólnych.

  • Współdziałanie. Ponieważ nazwy typów i zestawów programu .NET Framework są zawarte w kodzie XML, platformy inne niż program .NET Framework nie mogą uzyskać dostępu do danych wynikowych.

  • Wydajność. Zapisywanie typów i nazw zestawów znacznie zwiększa rozmiar wynikowego kodu XML.

Ten mechanizm jest podobny do serializacji binarnej lub SOAP używanej w zdalnej komunikacji programu .NET Framework (w szczególności BinaryFormatter i SoapFormatter).

Użycie NetDataContractSerializer jest podobne do użycia DataContractSerializer, z następującymi różnicami:

  • Konstruktory nie wymagają określenia typu bazowego. Można serializować dowolny typ przy użyciu tego samego wystąpienia NetDataContractSerializer.

  • Konstruktory nie akceptują listy znanych typów. Znany mechanizm typów jest niepotrzebny, jeśli nazwy typów są serializowane w formacie XML.

  • Konstruktory nie akceptują zastępczego kontraktu danych. Zamiast tego akceptują parametr ISurrogateSelector o nazwie surrogateSelector (który odpowiada właściwości SurrogateSelector). Jest to mechanizm dziedziczny zastępczy.

  • Konstruktory akceptują parametr nazwany assemblyFormat z FormatterAssemblyStyle, który odpowiada właściwości AssemblyFormat. Jak wspomniano wcześniej, może to służyć do zwiększenia możliwości przechowywania wersji serializatora. Jest to identyczne z mechanizmem FormatterAssemblyStyle w serializacji binarnej lub SOAP.

  • Konstruktory akceptują parametr StreamingContext o nazwie context, który jest przyporządkowany do właściwości Context. Możesz użyć tego do przekazywania informacji do typów będących serializowanych. To użycie jest identyczne z mechanizmem StreamingContext używanym w innych klasach System.Runtime.Serialization.

  • Metody Serialize i Deserialize to aliasy metod WriteObject i ReadObject. Modele te istnieją, aby zapewnić bardziej spójny model programowania przy użyciu serializacji binarnej lub SOAP.

Aby uzyskać więcej informacji na temat tych funkcji, zobacz Serializacja binarna.

Formaty XML używane przez NetDataContractSerializer i DataContractSerializer są zwykle niezgodne. Oznacza to, że próba serializacji przy użyciu jednego z tych serializatorów, a deserializacji z drugiego nie jest obsługiwanym scenariuszem.

Należy również pamiętać, że NetDataContractSerializer nie zwraca pełnego typu .NET Framework i nazwy zestawu dla każdego węzła w grafie obiektów. Dane wyjściowe są wyświetlane tylko w przypadku, gdy są niejednoznaczne. Oznacza to, że zwraca dane wyjściowe na poziomie obiektu głównego i dla wszystkich przypadków polimorficznych.

Zobacz też