Сериализация и десериализацияSerialization and Deserialization

Windows Communication Foundation (WCF) включает новый механизм DataContractSerializerсериализации.Windows Communication Foundation (WCF) includes a new serialization engine, the DataContractSerializer. DataContractSerializer Преобразование между объектами .NET Framework и XML в обоих направлениях.The DataContractSerializer translates between .NET Framework objects and XML, in both directions. В данном разделе объясняется, как работает сериализатор.This topic explains how the serializer works.

При сериализации .NET Framework объектов сериализатор понимает различные модели программирования сериализации, включая новую модель контракта данных .When serializing .NET Framework objects, the serializer understands a variety of serialization programming models, including the new data contract model. Полный список поддерживаемых типов см. в разделе Types Supported by the Data Contract Serializer.For a full list of supported types, see Types Supported by the Data Contract Serializer. Вводные сведения о контрактах данных см. в разделе Using Data Contracts.For an introduction to data contracts, see Using Data Contracts.

При десериализации XML-кода сериализатор использует классы XmlReader и XmlWriter .When deserializing XML, the serializer uses the XmlReader and XmlWriter classes. Он также поддерживает XmlDictionaryReader классы и XmlDictionaryWriter , позволяющие создавать оптимизированные XML-данные в некоторых случаях, например при использовании двоичного XML-формата WCF.It also supports the XmlDictionaryReader and XmlDictionaryWriter classes to enable it to produce optimized XML in some cases, such as when using the WCF binary XML format.

В NetDataContractSerializerсостав WCF также входит вспомогательный сериализатор,.WCF also includes a companion serializer, the NetDataContractSerializer. Объект NetDataContractSerializer аналогичен BinaryFormatter сериализаторам и SoapFormatter , так как он также выдает имена типов .NET Framework как часть сериализованных данных.The NetDataContractSerializer is similar to the BinaryFormatter and SoapFormatter serializers because it also emits .NET Framework type names as part of the serialized data. Он применяется при совместном использовании одних и тех же типов на концах сериализации и десериализации.It is used when the same types are shared on the serializing and the deserializing ends. Оба сериализатора ( DataContractSerializer и NetDataContractSerializer ) являются производными от общего базового класса XmlObjectSerializer.Both the DataContractSerializer and the NetDataContractSerializer derive from a common base class, the XmlObjectSerializer.

Предупреждение

Класс DataContractSerializer сериализует строки, содержащие управляющие символы с шестнадцатеричным значением меньше 20 в виде сущностей XML.The DataContractSerializer serializes strings containing control characters with a hexadecimal value below 20 as XML entities. Это может вызвать проблемы с клиентом, отличным от WCF, при отправке таких данных в службу WCF.This may cause a problem with a non-WCF client when sending such data to a WCF service.

Создание экземпляра DataContractSerializerCreating a DataContractSerializer Instance

Создание экземпляра DataContractSerializer является важным этапом.Constructing an instance of the DataContractSerializer is an important step. После создания экземпляра невозможно менять какие-либо настройки.After construction, you cannot change any of the settings.

Задание корневого типаSpecifying the Root Type

Корневой тип - это тип, экземпляры которого сериализуются и десериализуются.The root type is the type of which instances are serialized or deserialized. Сериализатор DataContractSerializer имеет множество перегрузок конструктора, однако хотя бы корневой тип должен предоставляться с использованием параметра type .The DataContractSerializer has many constructor overloads, but, at a minimum, a root type must be supplied using the type parameter.

Сериализатор, созданный для определенного корневого типа, невозможно использовать для сериализации (или десериализации) другого типа, если только он не наследуется от корневого типа.A serializer created for a certain root type cannot be used to serialize (or deserialize) another type, unless the type is derived from the root type. В следующем примере показаны два класса.The following example shows two classes.

[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 

Этот код создает экземпляр DataContractSerializer , который можно использовать только для сериализации и десериализации экземпляров класса Person .This code constructs an instance of the DataContractSerializer that can be used only to serialize or deserialize instances of the Person class.

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.

Задание известных типовSpecifying Known Types

Если в сериализуемых типах, которые еще не были обработаны с помощью атрибута KnownTypeAttribute или какого-либо другого механизма, применяется полиморфизм, то конструктору сериализатора с помощью параметра knownTypes необходимо передать список возможных известных типов.If polymorphism is involved in the types being serialized that is not already handled using the KnownTypeAttribute attribute or some other mechanism, a list of possible known types must be passed to the serializer’s constructor using the knownTypes parameter. Дополнительные сведения об известных типах см. в разделе Data Contract известные типы.For more information about known types, see Data Contract Known Types.

В следующем примере продемонстрирован класс LibraryPatron, который включает коллекцию указанного типа LibraryItem.The following example shows a class, LibraryPatron, that includes a collection of a specific type, the LibraryItem. Второй класс определяет тип LibraryItem .The second class defines the LibraryItem type. Третий и четвертый классы (Book и Newspaper) наследуются от класса LibraryItem .The third and four classes (Book and Newspaper) inherit from the LibraryItem class.

[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 

В следующем примере кода демонстрируется создание экземпляра сериализатора с использованием параметра knownTypes .The following code constructs an instance of the serializer using the knownTypes parameter.

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

Задание корневого имени и пространства имен по умолчаниюSpecifying the Default Root Name and Namespace

Как правило, при сериализации объекта имя и пространство имен по умолчанию внешнего элемента XML определяются в соответствии с именем и пространством имен контракта данных.Normally, when an object is serialized, the default name and namespace of the outermost XML element are determined according to the data contract name and namespace. Имена всех внутренних элементов определяются на основе имен членов данных, а их пространство имен представляет собой пространство имен контракта данных.The names of all inner elements are determined from data member names, and their namespace is the data contract’s namespace. В следующем примере задаются значения имени Name и пространства имен Namespace в конструкторах классов DataContractAttribute и DataMemberAttribute .The following example sets Name and Namespace values in the constructors of the DataContractAttribute and DataMemberAttribute classes.

[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 

В ходе сериализации экземпляра класса Person создается XML-код, подобный следующему.Serializing an instance of the Person class produces XML similar to the following.

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

Однако можно настроить имя и пространство имен корневого элемента по умолчанию, передав значения параметров rootName и rootNamespace конструктору DataContractSerializer .However, you can customize the default name and namespace of the root element by passing the values of the rootName and rootNamespace parameters to the DataContractSerializer constructor. Обратите внимание, что пространство имен rootNamespace не влияет на пространство имен содержащихся в нем элементов, которые соответствуют членам данных.Note that the rootNamespace does not affect the namespace of the contained elements that correspond to data members. Оно влияет только на пространство имен внешнего элемента.It affects only the namespace of the outermost element.

Эти значения могут быть переданы в виде строк или экземпляров класса XmlDictionaryString , что позволяет их оптимизировать с использованием двоичного XML-формата.These values can be passed as strings or instances of the XmlDictionaryString class to allow for their optimization using the binary XML format.

Задание максимальной квоты объектаSetting the Maximum Objects Quota

Некоторые перегрузки конструктора DataContractSerializer имеют параметр maxItemsInObjectGraph .Some DataContractSerializer constructor overloads have a maxItemsInObjectGraph parameter. Этот параметр определяет максимальное число объектов, которые сериализатор сериализует или десериализует за один вызов метода ReadObject .This parameter determines the maximum number of objects the serializer serializes or deserializes in a single ReadObject method call. (Этот метод всегда считывает один корневой объект, но в элементах данных этого объекта могут иметься другие объекты.(The method always reads one root object, but this object may have other objects in its data members. Эти объекты, в свою очередь, могут иметь в своем составе другие объекты и т. д.) Значение по умолчанию — 65536.Those objects may have other objects, and so on.) The default is 65536. Обратите внимание, что при сериализации или десериализации массивов каждая запись массива считается отдельным объектом.Note that when serializing or deserializing arrays, every array entry counts as a separate object. Также обратите внимание, что некоторые объекты могут иметь большое представление в памяти, поэтому одной этой квоты может быть недостаточно для предотвращения атак типа "отказ в обслуживании".Also, note that some objects may have a large memory representation, and so this quota alone may not be sufficient to prevent a denial of service attack. Дополнительные сведения см. в разделе вопросы безопасности данных.For more information, see Security Considerations for Data. При необходимости увеличить квоту по сравнению со значением по умолчанию следует увеличить ее как на стороне отправки (сериализации), так и на стороне получения (десериализации), так как квота действует как при считывании, так и при записи данных.If you need to increase this quota beyond the default value, it is important to do so both on the sending (serializing) and receiving (deserializing) sides because it applies to both when reading and writing data.

Циклы обработкиRound Trips

Цикл обработки совершается, когда объект десериализуется и повторно сериализуется за одну операцию.A round trip occurs when an object is deserialized and re-serialized in one operation. Таким образом, он перемещается от XML-кода к экземпляру объекта, а затем возвращается в поток XML.Thus, it goes from XML to an object instance, and back again into an XML stream.

Некоторые перегрузки конструктора DataContractSerializer имеют параметр ignoreExtensionDataObject , для которого по умолчанию задано значение false .Some DataContractSerializer constructor overloads have an ignoreExtensionDataObject parameter, which is set to false by default. В этом режиме по умолчанию данные можно без потерь отправлять в цикл обработки от новой версии контракта данных через старую версию обратно к новой при условии, что контракт данных реализует интерфейс IExtensibleDataObject .In this default mode, data can be sent on a round trip from a newer version of a data contract through an older version and back to the newer version without loss, as long as the data contract implements the IExtensibleDataObject interface. Предположим, например, что версия 1 контракта данных Person содержит члены данных Name и PhoneNumber , а версия 2 добавляет член Nickname .For example, suppose version 1 of the Person data contract contains the Name and PhoneNumber data members, and version 2 adds a Nickname member. Если реализуется объект IExtensibleDataObject , то при отправке информации из версии 2 в версию 1 данные Nickname сохраняются, а затем снова выдаются при повторной сериализации данных, поэтому данные не теряются при прохождении цикла обработки.If IExtensibleDataObject is implemented, when sending information from version 2 to version 1, the Nickname data is stored, and then re-emitted when the data is serialized again; therefore, no data is lost in the round trip. Дополнительные сведения см. в разделе контракты данных с прямыми совместимостью и Управление версиями контракта данных.For more information, see Forward-Compatible Data Contracts and Data Contract Versioning.

Проблемы безопасности и допустимости схемы в циклах обработкиSecurity and Schema Validity Concerns with Round Trips

Циклы обработки влияют на безопасность.Round trips may have security implications. Например, десериализация и сохранение больших объемов лишних данных могут представлять угрозу безопасности.For example, deserializing and storing large amounts of extraneous data may be a security risk. Проблемы безопасности при повторной выдаче этих данных обусловлены невозможностью провести проверку, особенно если в процессе задействованы цифровые сигнатуры.There may be security concerns about re-emitting this data that there is no way to verify, especially if digital signatures are involved. Например, в вышеприведенном сценарии конечная точка версии 1 могла бы подписать значение Nickname , которое содержит вредоносные данные.For example, in the previous scenario, the version 1 endpoint could be signing a Nickname value that contains malicious data. Наконец, могут возникнуть проблемы с допустимостью схемы: конечная точка может всегда выдавать только данные, которые строго соответствуют ее заявленному контракту, и не поддерживать других значений.Finally, there may be schema validity concerns: an endpoint may want to always emit data that strictly adheres to its stated contract and not any extra values. В предыдущем примере в контракте конечной точки версии 1 говорится, что точка выдает только данные Name и PhoneNumber, а при использовании проверки допустимости схемы выдача дополнительного значения Nickname приводит к сбою проверки.In the previous example, the version 1 endpoint’s contract says that it emits only Name and PhoneNumber, and if schema validation is being used, emitting the extra Nickname value causes validation to fail.

Включение и отключение циклов обработкиEnabling and Disabling Round Trips

Для отключения циклов обработки не реализуйте интерфейс IExtensibleDataObject .To turn off round trips, do not implement the IExtensibleDataObject interface. Если невозможно управлять типами, для достижения такого же эффекта необходимо задать параметру ignoreExtensionDataObject значение true .If you have no control over the types, set the ignoreExtensionDataObject parameter to true to achieve the same effect.

Сохранение графа объектаObject Graph Preservation

Как правило, удостоверение объекта не имеет значения для сериализатора, как показано в следующем примере кода.Normally, the serializer does not care about object identity, as in the following code.

[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 

В следующем примере кода демонстрируется создание заказа на покупку.The following code creates a purchase order.

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

Обратите внимание, что полям billTo и shipTo задан один и тот же экземпляр объекта.Notice that billTo and shipTo fields are set to the same object instance. Однако созданный XML-код дублирует повторяющуюся информацию и по внешнему виду аналогичен следующему XML-коду.However, the generated XML duplicates the information duplicated, and looks similar to the following XML.

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

Этот подход имеет следующие характеристики, которые могут оказаться нежелательными.However, this approach has the following characteristics, which may be undesirable:

  • Производительность.Performance. Репликация данных неэффективна.Replicating data is inefficient.

  • Циклические ссылки.Circular references. Если объекты ссылаются на самих себя (даже через другие объекты), сериализация результатов репликации приводит к бесконечному циклу.If objects refer to themselves, even through other objects, serializing by replication results in an infinite loop. (В этом случае сериализатор создает исключение SerializationException .)(The serializer throws a SerializationException if this happens.)

  • Семантика.Semantics. Иногда очень важно сохранить отнесение двух ссылок к одному и тому же объекту, а не к двум идентичным объектам.Sometimes it is important to preserve the fact that two references are to the same object, and not to two identical objects.

По этим причинам некоторые перегрузки конструктора DataContractSerializer имеют параметр preserveObjectReferences (по умолчанию ему задано значение false).For these reasons, some DataContractSerializer constructor overloads have a preserveObjectReferences parameter (the default is false). Если для trueэтого параметра задано значение, то используется специальный метод кодирования ссылок на объекты, распознаваемый только WCF.When this parameter is set to true, a special method of encoding object references, which only WCF understands, is used. Если задано значение true, пример XML-кода выглядит следующим образом.When set to true, the XML code example now resembles the following.

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

Пространство имен "SER" относится к стандартному пространству имен http://schemas.microsoft.com/2003/10/Serialization/сериализации.The "ser" namespace refers to the standard serialization namespace, http://schemas.microsoft.com/2003/10/Serialization/. Каждый блок данных сериализуется только один раз, ему присваивается идентификационный номер, и при использовании этих блоков в будущем создается ссылка на уже сериализованные данные.Each piece of data is serialized only once and given an ID number, and subsequent uses result in a reference to the already serialized data.

Важно!

Если в контракте данных XMLElementприсутствует и атрибут "id", и атрибут "ref", используется атрибут "ref", а атрибут "id" игнорируется.If both "id" and "ref" attributes are present in the data contract XMLElement, then the "ref" attribute is honored and the "id" attribute is ignored.

Важно понимать существующие в этом режиме ограничения.It is important to understand the limitations of this mode:

  • XML-код, создаваемый сериализатором DataContractSerializer с помощью preserveObjectReferences с заданным значением true , не поддерживает возможность взаимодействия с какими-либо другими технологиями, доступ к нему может осуществлять только другой экземпляр DataContractSerializer , для preserveObjectReferences которого также задано значение true.The XML the DataContractSerializer produces with preserveObjectReferences set to true is not interoperable with any other technologies, and can be accessed only by another DataContractSerializer instance, also with preserveObjectReferences set to true.

  • Эта функция не обеспечивается поддержкой метаданных (схемы).There is no metadata (schema) support for this feature. Созданная схема действительна, только если preserveObjectReferences задано значение false.The schema that is produced is valid only for the case when preserveObjectReferences is set to false.

  • Эта функция может замедлить процесс сериализации и десериализации.This feature may cause the serialization and deserialization process to run slower. Несмотря на отсутствие необходимости в репликации данных, в этом режиме необходимо выполнять дополнительные сравнения объектов.Although data does not have to be replicated, extra object comparisons must be performed in this mode.

Внимание!

Если включен режим preserveObjectReferences , очень важно задать правильную квоту значению maxItemsInObjectGraph .When the preserveObjectReferences mode is enabled, it is especially important to set the maxItemsInObjectGraph value to the correct quota. Особенности обработки массивов в этом режиме позволяют злоумышленнику легко создавать небольшое вредоносное сообщение, которое приводит к расходованию больших объемов памяти, и препятствовать этому можно только заданием квоты maxItemsInObjectGraph .Due to the way arrays are handled in this mode, it is easy for an attacker to construct a small malicious message that results in large memory consumption limited only by the maxItemsInObjectGraph quota.

Задание суррогата контракта данныхSpecifying a Data Contract Surrogate

Некоторые перегрузки конструктора DataContractSerializer имеют параметр dataContractSurrogate , для которого может быть задано значение null.Some DataContractSerializer constructor overloads have a dataContractSurrogate parameter, which may be set to null. В противном случае можно использовать этот параметр для задания суррогата контракта данных, представляющего собой тип, который реализует интерфейс IDataContractSurrogate .Otherwise, you can use it to specify a data contract surrogate, which is a type that implements the IDataContractSurrogate interface. Затем с помощью интерфейса можно настроить процесс сериализации и десериализации.You can then use the interface to customize the serialization and deserialization process. Дополнительные сведения см. в разделе суррогаты контракта данных.For more information, see Data Contract Surrogates.

СериализацияSerialization

Следующая информация применима к любому классу, наследуемому от сериализатора XmlObjectSerializer, включая классы DataContractSerializer и NetDataContractSerializerThe following information applies to any class that inherits from the XmlObjectSerializer, including the DataContractSerializer and NetDataContractSerializer classes.

Простая сериализацияSimple Serialization

Основным способом сериализации объекта является его передача методу WriteObject .The most basic way to serialize an object is to pass it to the WriteObject method. Существует три перегрузки, каждая из которых предназначена для записи в Stream, XmlWriterили XmlDictionaryWriterсоответственно.There are three overloads, one each for writing to a Stream, an XmlWriter, or an XmlDictionaryWriter. При использовании перегрузки Stream на выходе получается XML-код в кодировке UTF-8.With the Stream overload, the output is XML in the UTF-8 encoding. При использовании перегрузки XmlDictionaryWriter сериализатор оптимизирует объекты на выходе для двоичного XML-формата.With the XmlDictionaryWriter overload, the serializer optimizes its output for binary XML.

При использовании WriteObject метода сериализатор использует имя и пространство имен по умолчанию для элемента-оболочки и записывает его вместе с содержимым (см. предыдущий раздел "Указание корневого имени и пространства имен по умолчанию").When using the WriteObject method, the serializer uses the default name and namespace for the wrapper element and writes it out along with the contents (see the previous "Specifying the Default Root Name and Namespace" section).

Следующий пример демонстрирует запись с помощью XmlDictionaryWriter.The following example demonstrates writing with an 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)

Это позволяет создать XML-код, подобный следующему.This produces XML similar to the following.

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

Пошаговая сериализацияStep-By-Step Serialization

Для записи конечного элемента и содержимого объекта и закрытия программы-оболочки используйте методы WriteStartObject, WriteObjectContentи WriteEndObject соответственно.Use the WriteStartObject, WriteObjectContent, and WriteEndObject methods to write the end element, write the object contents, and close the wrapper element, respectively.

Примечание

Перегрузок этих методов Stream нет.There are no Stream overloads of these methods.

Описанная здесь пошаговая сериализация, как правило, используется в двух случаях.This step-by-step serialization has two common uses. Во-первых, чтобы вставлять содержимое, такое как атрибуты или комментарии, между WriteStartObject и WriteObjectContent, как показано в следующем примере.One is to insert contents such as attributes or comments between WriteStartObject and WriteObjectContent, as shown in the following example.

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)

Это позволяет создать XML-код, подобный следующему.This produces XML similar to the following.

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

Во-вторых, пошаговая сериализация часто применяется, чтобы полностью избежать использования WriteStartObject и WriteEndObject и создать собственную пользовательскую программу-оболочку (или пропустить создание программы-оболочки в принципе), как показано в следующем примере кода.Another common use is to avoid using WriteStartObject and WriteEndObject entirely, and to write your own custom wrapper element (or even skip writing a wrapper altogether), as shown in the following code.

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

Это позволяет создать XML-код, подобный следующему.This produces XML similar to the following.

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

Примечание

Использование пошаговой сериализации может привести к созданию XML-кода с недействительной схемой.Using step-by-step serialization may result in schema-invalid XML.

ДесериализацияDeserialization

Следующая информация применима к любому классу, наследуемому от сериализатора XmlObjectSerializer, включая классы DataContractSerializer и NetDataContractSerializerThe following information applies to any class that inherits from the XmlObjectSerializer, including the DataContractSerializer and NetDataContractSerializer classes.

Основным способом десериализации объекта является вызов одной из перегрузок метода ReadObject .The most basic way to deserialize an object is to call one of the ReadObject method overloads. Существует три перегрузки, каждая из которых предназначена для считывания с помощью XmlDictionaryReader, XmlReaderили Streamсоответственно.There are three overloads, one each for reading with a XmlDictionaryReader, an XmlReader, or a Stream. Обратите внимание, что перегрузка Stream создает текстовое средство чтения XmlDictionaryReader , не защищенное никакими квотами, следовательно, его нужно использовать только для чтения надежных данных.Note that the Stream overload creates a textual XmlDictionaryReader that is not protected by any quotas, and should be used only to read trusted data.

Также обратите внимание, что объект, возвращаемый методом ReadObject , должен быть приведен к соответствующему типу.Also note that the object the ReadObject method returns must be cast to the appropriate type.

В следующем примере кода демонстрируется создание экземпляра сериализатора DataContractSerializer и средства чтения XmlDictionaryReader, а также последующая десериализация экземпляра Person .The following code constructs an instance of the DataContractSerializer and an XmlDictionaryReader, then deserializes a Person instance.

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)

Прежде чем вызывать метод ReadObject , необходимо разместить средство чтения XML в программе-оболочке или не имеющем содержимого узле, который предшествует программе-оболочке.Before calling the ReadObject method, position the XML reader on the wrapper element or on a non-content node that precedes the wrapper element. Это можно осуществить вызовом метода Read устройства чтения XmlReader или производного метода и тестированием типа NodeType, как показано в следующем примере кода.You can do this by calling the Read method of the XmlReader or its derivation, and testing the NodeType, as shown in the following code.

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

Обратите внимание, что до передачи устройства чтения методу ReadObjectв этой программе-оболочке можно прочитать атрибуты.Note that you can read attributes on this wrapper element before handing the reader to ReadObject.

При использовании одной из простых ReadObject перегрузок десериализатор ищет имя и пространство имен по умолчанию в элементе-оболочке (см. предыдущий раздел "Указание корневого имени и пространства имен по умолчанию") и создает исключение, если обнаруживается неизвестный дерев.When using one of the simple ReadObject overloads, the deserializer looks for the default name and namespace on the wrapper element (see the preceding section, "Specifying the Default Root Name and Namespace") and throws an exception if it finds an unknown element. Предполагается, что в предыдущем примере используется программа-оболочка <Person> .In the preceding example, the <Person> wrapper element is expected. Метод IsStartObject вызывается, чтобы убедиться, что средство чтения размещено в элементе, именованном как ожидается.The IsStartObject method is called to verify that the reader is positioned on an element that is named as expected.

Можно отключить такую проверку имени программы-оболочки; некоторые перегрузки метода ReadObject используют логический параметр verifyObjectName, которому по умолчанию задано значение true .There is a way to disable this wrapper element name check; some overloads of the ReadObject method take the Boolean parameter verifyObjectName, which is set to true by default. Если параметру задано значение false, имя и пространство имен программы-оболочки игнорируются.When set to false, the name and namespace of the wrapper element is ignored. Эта функция полезна при чтении XML-кода, который был создан с использованием механизма пошаговой сериализации, описанной ранее.This is useful for reading XML that was written using the step-by-step serialization mechanism described previously.

Использование NetDataContractSerializerUsing the NetDataContractSerializer

Основное DataContractSerializer различие между NetDataContractSerializer и состоит в том, что DataContractSerializer NetDataContractSerializer использует имена контрактов данных, а выходные данные полны .NET Framework сборки и имена типов в сериализованном XML.The primary difference between the DataContractSerializer and the NetDataContractSerializer is that the DataContractSerializer uses data contract names, whereas the NetDataContractSerializer outputs full .NET Framework assembly and type names in the serialized XML. Это означает, что одни и те же типы должны совместно использоваться конечными точками сериализации и десериализации.This means that the exact same types must be shared between the serialization and deserialization endpoints. Так как при использовании сериализатора NetDataContractSerializer всегда известны точные типы, которые должны быть десериализованы, механизм известных типов не требуется.This means that the known types mechanism is not required with the NetDataContractSerializer because the exact types to be deserialized are always known.

Однако в связи с этим могут возникнуть некоторые проблемы:However, several problems can occur:

  • Безопасность.Security. Загружается любой тип, обнаруживаемый в десериализуемом XML-коде.Any type found in the XML being deserialized is loaded. Этим можно воспользоваться для принудительной загрузки вредоносных типов.This can be exploited to force the loading of malicious types. Использование сериализатора NetDataContractSerializer с ненадежными данными возможно только при условии применения связывателя сериализации (с использованием свойства Binder или параметра конструктора).Using the NetDataContractSerializer with untrusted data should be done only if a Serialization Binder is used (using the Binder property or constructor parameter). Связыватель позволяет загружать только надежные типы.The binder permits only safe types to be loaded. Механизм связывателя аналогичен используемому типами в пространстве имен System.Runtime.Serialization .The Binder mechanism is identical to the one that types in the System.Runtime.Serialization namespace use.

  • Управление версиями.Versioning. Использование полных имен типов и сборок в XML-коде строго ограничивает возможности управления версиями типов.Using full type and assembly names in the XML severely restricts how types can be versioned. При этом невозможно изменить имена типов, пространства имен, имена и версии сборок.The following cannot be changed: type names, namespaces, assembly names, and assembly versions. Задание свойству AssemblyFormat или параметру конструктора значения Simple вместо значения по умолчанию Full позволяет изменить версию сборки, но не универсальные типы параметров.Setting the AssemblyFormat property or constructor parameter to Simple instead of the default value of Full allows for assembly version changes, but not for generic parameter types.

  • Взаимодействие.Interoperability. Поскольку тип .NET Framework и имена сборок включены в XML, платформы, отличные от .NET Framework, не могут получить доступ к результирующим данным.Because .NET Framework type and assembly names are included in the XML, platforms other than the .NET Framework cannot access the resulting data.

  • Производительность.Performance. Запись имен типов и сборок значительно увеличивает размер получаемого XML-кода.Writing out the type and assembly names significantly increases the size of the resulting XML.

Этот механизм похож на двоичную или SOAP-сериализацию, используемую .NET Framework удаленным взаимодействием ( SoapFormatterв BinaryFormatter частности, и).This mechanism is similar to binary or SOAP serialization used by .NET Framework remoting (specifically, the BinaryFormatter and the SoapFormatter).

Сериализаторы NetDataContractSerializer и DataContractSerializerиспользуются практически аналогично со следующими отличиями.Using the NetDataContractSerializer is similar to using the DataContractSerializer, with the following differences:

  • Конструкторы не требуют указания корневого типа.The constructors do not require you to specify a root type. Любой тип можно сериализовать с помощью одного и того же экземпляра сериализатора NetDataContractSerializer.You can serialize any type with the same instance of the NetDataContractSerializer.

  • Конструкторы не принимают список известных типов.The constructors do not accept a list of known types. Если имена типов сериализуются в XML-код, механизм известных типов не требуется.The known types mechanism is unnecessary if type names are serialized into the XML.

  • Конструкторы не принимают суррогаты контракта данных.The constructors do not accept a data contract surrogate. Вместо этого они принимают параметр ISurrogateSelector , называемый surrogateSelector (который сопоставляется со свойством SurrogateSelector ).Instead, they accept an ISurrogateSelector parameter called surrogateSelector (which maps to the SurrogateSelector property). Это устаревший суррогатный механизм.This is a legacy surrogate mechanism.

  • Конструкторы принимают параметр стиля assemblyFormat , называемый FormatterAssemblyStyle , который сопоставляется со свойством AssemblyFormat .The constructors accept a parameter called assemblyFormat of the FormatterAssemblyStyle that maps to the AssemblyFormat property. Как уже отмечалось ранее, эту особенность можно использовать для расширения возможностей сериализатора по управлению версиями.As discussed previously, this can be used to enhance the versioning capabilities of the serializer. Этот подход аналогичен механизму FormatterAssemblyStyle в двоичной сериализации или сериализации SOAP.This is identical to the FormatterAssemblyStyle mechanism in binary or SOAP serialization.

  • Конструкторы принимают параметр StreamingContext , называемый context , который сопоставляется со свойством Context .The constructors accept a StreamingContext parameter called context that maps to the Context property. Эту особенность можно использовать для передачи информации в сериализуемые типы.You can use this to pass information into types being serialized. Данный подход аналогичен использованию механизма StreamingContext в других классах System.Runtime.Serialization .This usage is identical to that of the StreamingContext mechanism used in other System.Runtime.Serialization classes.

  • Методы Serialize и Deserialize представляют собой псевдонимы для методов WriteObject и ReadObject .The Serialize and Deserialize methods are aliases for the WriteObject and ReadObject methods. Они предоставляют более последовательную модель программирования с двоичной сериализацией или сериализацией SOAP.These exist to provide a more consistent programming model with binary or SOAP serialization.

Дополнительные сведения об этих функциях см. в разделе двоичная сериализация.For more information about these features, see Binary Serialization.

Форматы XML, используемые сериализаторами NetDataContractSerializer и DataContractSerializer , как правило, не совместимы.The XML formats that the NetDataContractSerializer and the DataContractSerializer use are normally not compatible. Следовательно, возможность сериализации с помощью одного из них и десериализации с помощью другого не поддерживается.That is, attempting to serialize with one of these serializers and deserialize with the other is not a supported scenario.

Кроме того, обратите NetDataContractSerializer внимание, что параметр не выводит полный тип .NET Framework и имя сборки для каждого узла в графе объекта.Also, note that the NetDataContractSerializer does not output the full .NET Framework type and assembly name for each node in the object graph. Он выводит эту информацию, только если она неоднозначна.It outputs that information only where it is ambiguous. Таким образом, вывод данных осуществляется на уровне корневого объекта и для любых полиморфных случаев.That is, it outputs at the root object level and for any polymorphic cases.

См. такжеSee also