Serialization 및 Deserialization

WCF(Windows Communication Foundation)에는 새 serialization 엔진인 DataContractSerializer가 포함되어 있습니다. DataContractSerializer는 .NET Framework 개체와 XML을 양방향으로 변환합니다. 이 항목에서는 serializer가 작동하는 방식에 대해 설명합니다.

.NET Framework 개체를 직렬화할 때 직렬 변환기는 새로운 데이터 계약 모델을 비롯한 다양한 serialization 프로그래밍 모델을 이해합니다. 지원되는 형식의 전체 목록은 Types Supported by the Data Contract Serializer을 참조하십시오. 데이터 계약에 대한 소개는 Using Data Contracts을 참조하십시오.

XML을 역직렬화할 때 직렬 변환기에서는 XmlReaderXmlWriter 클래스를 사용합니다. 또한 XmlDictionaryReaderXmlDictionaryWriter 클래스를 지원하여 WCF 이진 XML 형식을 사용하는 경우와 같은 일부 경우에 최적화된 XML을 생성할 수 있습니다.

또한 WCF에는 도우미 serializer인 NetDataContractSerializer가 있습니다. NetDataContractSerializer :

  • 안전하지 않습니다. 자세한 내용은 BinaryFormatter 보안 가이드를 참조하세요.
  • 직렬화된 데이터의 일부로 .NET Framework 형식 이름도 내보내므로 BinaryFormatterSoapFormatter 직렬 변환기와 유사합니다.
  • 그리고 직렬화 측과 역직렬화 측에서 동일한 형식을 공유할 때 사용됩니다.

DataContractSerializerNetDataContractSerializer 모두 공통 기본 클래스인 XmlObjectSerializer에서 파생됩니다.

Warning

DataContractSerializer 는 20 미만의 16진수 값이 있는 제어 문자를 포함하는 문자열을 XML 엔터티로 serialize합니다. 이 경우 WCF 클라이언트가 아닌 클라이언트가 이러한 데이터를 WCF 서비스로 보내는 문제가 발생할 수 있습니다.

DataContractSerializer 인스턴스 만들기

DataContractSerializer 의 인스턴스를 구성하는 작업은 중요한 단계입니다. 구성한 후에는 어떤 설정도 변경할 수 없습니다.

루트 형식 지정

루트 형식 은 직렬화 또는 역직렬화되는 인스턴스의 형식입니다. DataContractSerializer 에는 여러 개의 생성자 오버로드가 있지만, 최소한 type 매개 변수를 사용하여 루트 형식을 지정해야 합니다.

특정 루트 형식에 대해 만들어진 serializer는 해당 형식이 루트 형식에서 파생된 경우가 아니라면 다른 형식을 직렬화하거나 역직렬화하는 데 사용할 수 없습니다. 다음 예제에서는 두 개의 클래스가 나옵니다.

[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 의 인스턴스를 구성합니다.

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.

알려진 형식 지정

Serialize되는 형식 중 KnownTypeAttribute 특성이나 일부 다른 메커니즘을 사용하여 아직 처리되지 않은 형식에 다형성이 포함되는 경우, 알려진 가능한 형식 목록을 knownTypes 매개 변수를 사용하여 serializer의 생성자에게 전달해야 합니다. 알려진 형식에 대한 자세한 내용은 데이터 계약의 알려진 형식을 참조하세요.

다음 예제에서는 특정 형식의 컬렉션인 LibraryPatron이 포함된 클래스 LibraryItem을 보여 줍니다. 두 번째 클래스는 LibraryItem 형식을 정의합니다. 세 번째와 네 번째 클래스인BookNewspaperLibraryItem 클래스에서 상속됩니다.

[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 매개 변수를 사용하여 serializer의 인스턴스를 구성합니다.

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

기본 루트 이름 및 네임스페이스 지정

일반적으로 개체가 serialize되면 데이터 계약 이름과 네임스페이스에 따라 가장 바깥쪽 XML 요소의 기본 이름과 네임스페이스가 결정됩니다. 모든 내부 요소의 이름은 데이터 멤버 이름을 따르며 해당 네임스페이스는 데이터 계약의 네임스페이스와 동일합니다. 다음 예제에서는 NameNamespace 클래스의 생성자에 DataContractAttributeDataMemberAttribute 값을 설정합니다.

[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 클래스의 인스턴스를 serialize하면 다음과 유사한 XML이 생성됩니다.

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

그러나 rootNamerootNamespace 매개 변수의 값을 DataContractSerializer 생성자에 전달하여 루트 요소의 기본 이름과 네임스페이스를 사용자 지정할 수 있습니다. rootNamespace 는 데이터 멤버에 해당하는 포함된 요소의 네임스페이스에 영향을 주지 않고, 가장 바깥쪽 요소의 네임스페이스에만 영향을 줍니다.

이러한 값은 XmlDictionaryString 클래스의 인스턴스나 문자열로 전달하여, 이진 XML 형식을 통해 최적화할 수 있습니다.

최대 개체 할당량 설정

일부 DataContractSerializer 생성자 오버로드에는 maxItemsInObjectGraph 매개 변수가 있습니다. 이 매개 변수는 직렬 변환기가 단일 ReadObject 메서드 호출에서 직렬화하거나 역직렬화하는 최대 개체 수를 결정합니다. (이 메서드는 항상 하나의 루트 개체를 읽지만 이 개체의 데이터 멤버에는 다른 개체가 있을 수 있습니다. 이러한 개체에는 다른 개체가 있을 수 있습니다.) 기본값은 65536입니다. 배열을 직렬화하거나 역직렬화할 때 모든 배열 항목은 개별 개체로 계산됩니다. 또한 일부 개체에는 큰 메모리 표현이 있을 수 있으므로 서비스 거부 공격을 방지하기에 이 할당량만으로 충분하지 않을 수 있습니다. 자세한 내용은 데이터에 대한 보안 고려 사항을 참조하세요. 이 할당량을 기본값보다 크게 늘려야 할 경우 데이터를 읽고 쓸 때 보내는 측(직렬화)과 받는 측(역직렬화)에 모두 적용되므로 양측에서 값을 늘려야 합니다.

라운드트립

라운드트립 은 개체가 한 번에 역직렬화 및 다시 직렬화될 때 발생합니다. 예를 들면 XML에서 개체 인스턴스로 이동하고 다시 XML 스트림으로 이동합니다.

일부 DataContractSerializer 생성자 오버로드에는 기본적으로 ignoreExtensionDataObject 로 설정된 false 매개 변수가 있습니다. 이 기본 모드에서는 데이터 계약에서 IExtensibleDataObject 인터페이스를 구현하는 한, 라운드트립 시 이전 버전을 통해 최신 버전의 데이터 계약에서 다시 이 최신 버전으로 데이터 손실 없이 데이터를 보낼 수 있습니다. 예를 들어 Person 데이터 계약의 버전 1에 NamePhoneNumber 데이터 멤버가 들어 있고 버전 2에서 Nickname 멤버를 추가한다고 가정합니다. IExtensibleDataObject 가 구현되면 버전 2에서 버전 1로 정보를 보낼 때 Nickname 데이터가 저장된 다음 다시 serialize될 때 다시 내보내집니다. 따라서 라운드트립 시 데이터가 손실되지 않습니다. 자세한 내용은 이후 버전과 호환되는 데이터 계약데이터 계약 버전 관리를 참조하세요.

라운드트립과 관련된 보안 및 스키마 유효성 검사 문제

라운드트립 시 보안 관련 문제가 있을 수 있습니다. 예를 들어 잘못 사용된 엄청난 양의 데이터를 역직렬화하고 저장하면 보안상 위험이 따를 수 있습니다. 특히 디지털 서명이 포함된 경우 확인할 방법이 없는 이러한 데이터를 다시 내보낼 경우 보안 문제가 있을 수 있습니다. 예를 들면 이전 시나리오의 경우 버전 1 엔드포인트에서 악의적인 데이터가 포함된 Nickname 값을 서명할 수 있습니다. 마지막으로 스키마 유효성 검사 문제가 있을 수 있는데, 엔드포인트에서 명시된 계약을 엄격히 준수하는 데이터를 항상 내보내고 다른 값은 내보내지 않을 수 있습니다. 이전 예제의 경우 버전 1 엔드포인트 계약에는 NamePhoneNumber만 내보낸다고 명시되어 있는데, 스키마 유효성 검사를 사용 중일 때 추가 Nickname 값을 내보내면 유효성 검사에 실패하게 됩니다.

라운드트립 사용 및 사용 안 함

라운드트립을 해제하려면 IExtensibleDataObject 인터페이스를 구현하지 않도록 합니다. 형식에 대한 제어 권한이 없을 경우 ignoreExtensionDataObject 매개 변수를 true 로 설정하여 동일한 결과를 얻을 수 있습니다.

개체 그래프 유지

다음 코드에서처럼 serializer는 일반적으로 개체 ID를 고려하지 않습니다.

[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

다음 코드에서는 구매 주문서를 만듭니다.

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

billToshipTo 필드는 동일한 개체 인스턴스에 설정됩니다. 그러나 생성된 XML은 복제된 정보를 복제하는데, 다음과 유사한 XML이 생성됩니다.

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

그러나 이 방법은 다음과 같이 바람직하지 않은 특성을 갖습니다.

  • 성능. 데이터 복제가 비효율적입니다.

  • 순환 참조. 개체가 다른 개체를 통해 자신을 참조하는 경우에도, 복제로 serialize하면 무한 루프가 발생합니다. 이 경우 serializer가 SerializationException 을 throw합니다.

  • 의미. 경우에 따라 두 개의 참조가 똑같은 두 개의 개체로 이루어지는 것이 아니라, 하나의 동일한 개체로 이루어지도록 하는 것이 중요합니다.

이러한 이유로 인해, 일부 DataContractSerializer 생성자 오버로드에는 기본값이 preserveObjectReferencesfalse매개 변수가 있습니다. 이 매개 변수를 true로 설정하면, WCF에서만 인식하는 개체 참조를 인코딩하는 특수한 메서드가 사용됩니다. true로 설정된 경우 XML 코드 예제는 이제 다음과 유사합니다.

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

"ser" 네임스페이스는 표준 serialization 네임스페이스(http://schemas.microsoft.com/2003/10/Serialization/)를 나타냅니다. 각 데이터 부분은 단 한 번만 serialize되고 이 데이터에 ID 번호가 제공됩니다. 이후 사용에서는 이미 serialize된 데이터를 참조하게 됩니다.

Important

데이터 계약 XMLElement에 "id" 및 "ref" 특성이 모두 있으면 "ref" 특성이 적용되는 "id" 특성은 무시됩니다.

이 모드의 제한 사항을 파악하는 것도 중요합니다.

  • DataContractSerializerpreserveObjectReferences 로 설정된 상태에서, true 에서 생성한 XML은 다른 기술과 상호 운용할 수 없고, 또한 DataContractSerializerpreserveObjectReferences 로 설정된 상태에서는 다른 true인스턴스에서만 이 XML에 액세스할 수 있습니다.

  • 이 기능에 대해서는 메타데이터(스키마)가 지원되지 않습니다. 생성된 스키마는 preserveObjectReferencesfalse로 설정된 경우에만 유효합니다.

  • 이 기능으로 인해 serialization 및 deserialization 프로세스 실행 속도가 느려질 수 있습니다. 데이터를 복제하지 않아도 되지만 이 모드에서 추가 개체 비교를 수행해야 합니다.

주의

preserveObjectReferences 모드를 사용하는 경우, maxItemsInObjectGraph 값을 올바른 할당량으로 설정하는 것이 특히 중요합니다. 이 모드에서 배열이 처리되는 방식 때문에, 공격자가 maxItemsInObjectGraph 할당량에 의해서만 제한되는 과다한 메모리 소비를 일으키는 작은 악의적인 메시지를 쉽게 작성할 수 있습니다.

데이터 계약 서로게이트 지정

일부 DataContractSerializer 생성자 오버로드에는 dataContractSurrogate 로 설정될 수 있는 null매개 변수가 있습니다. 그렇지 않으면 이 오버로드를 사용하여 인터페이스를 구현하는 형식인데이터 계약 서로게이트 IDataContractSurrogate 를 지정할 수 있습니다. 그러면 인터페이스를 사용하여 serialization 및 deserialization 프로세스를 사용자 지정할 수 있습니다. 자세한 내용은 데이터 계약 서로게이트를 참조하세요.

직렬화

다음과 같은 정보는 XmlObjectSerializerDataContractSerializer 클래스를 포함하여, NetDataContractSerializer 에서 상속되는 모든 클래스에 적용됩니다.

간단한 Serialization

개체를 serialize하는 가장 기본적인 방법은 개체를 WriteObject 메서드에 전달하는 것입니다. 그리고 세 개의 오버로드가 있는데, 각각 Stream, XmlWriter또는 XmlDictionaryWriter에 쓰기 위한 오버로드입니다. Stream 오버로드를 사용하면 UTF-8 인코딩 형식의 XML로 출력됩니다. XmlDictionaryWriter 오버로드를 사용하면 serializer는 이진 XML에 대한 출력을 최적화합니다.

WriteObject 메서드를 사용할 때, serializer는 래퍼 요소에 기본 이름 및 네임스페이스를 사용하고, 콘텐츠와 함께 래퍼 요소를 작성합니다. 이전 단원인 "기본 루트 이름 및 네임스페이스 지정"을 참조하세요.

다음 예제에서는 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이 생성됩니다.

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

단계별 Serialization

각각 끝 요소를 작성하고 개체 내용을 작성하며 래퍼 요소를 닫으려면, WriteStartObject, WriteObjectContentWriteEndObject 메서드를 사용합니다.

참고 항목

이러한 메서드의 Stream 오버로드가 없습니다.

이러한 단계별 serialization을 사용하는 방법은 일반적으로 두 가지가 있습니다. 한 가지 방법은 다음 예와 같이, 특성 또는 설명 등의 내용을 WriteStartObjectWriteObjectContent사이에 삽입하는 방법입니다.

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이 생성됩니다.

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

또 다른 일반적인 방법은 다음 코드에서처럼, WriteStartObjectWriteEndObject 를 전혀 사용하지 않고 사용자 지정 래퍼 요소를 작성하거나 래퍼 작성도 함께 건너뛰는 방법입니다.

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

이렇게 하면 다음과 유사한 XML이 생성됩니다.

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

참고 항목

단계별 serialization을 사용하면 스키마에 유효하지 않은 XML이 생성될 수 있습니다.

Deserialization

다음과 같은 정보는 XmlObjectSerializerDataContractSerializer 클래스를 포함하여, NetDataContractSerializer 에서 상속되는 모든 클래스에 적용됩니다.

개체를 역직렬화하는 가장 기본적인 방법은 ReadObject 메서드 오버로드 중 하나를 호출하는 것입니다. 그리고 세 개의 오버로드가 있는데, 이 각 오버로드는 XmlDictionaryReader, XmlReader또는 Stream을 사용하여 읽기 위한 오버로드입니다. Stream 오버로드는 할당량으로 보호되지 않고 신뢰할 수 있는 데이터를 읽는 데만 사용해야 하는 텍스트 XmlDictionaryReader 를 만듭니다.

또한 ReadObject 메서드가 반환하는 개체는 적합한 형식으로 캐스팅되어야 합니다.

다음 코드에서는 DataContractSerializerXmlDictionaryReader의 인스턴스를 구성하고 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)

ReadObject 메서드를 호출하기 전에, 래퍼 요소나 래퍼 요소 앞에 오는 비콘텐츠 노드에 XML 판독기를 배치합니다. 이렇게 하려면 다음 코드에서처럼, ReadXmlReader 메서드 또는 해당 파생 항목을 호출하고 NodeType을 테스트하면 됩니다.

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로 판독기를 전달하기 전에 이 래퍼 요소의 특성을 읽을 수 있습니다.

간단한 ReadObject 오버로드 중 하나를 사용할 때, 역직렬 변환기는 래퍼 요소의 기본 이름과 네임스페이스를 찾고(이전 단원 "기본 루트 이름 및 네임스페이스 지정" 참조) 알 수 없는 요소를 찾은 경우 예외를 throw합니다. 이전 예에서는 <Person> 래퍼 요소가 필요합니다. 판독기가 명명된 요소에 예상한 대로 배치되었는지 확인하기 위해 IsStartObject 메서드가 호출됩니다.

이 래퍼 요소 이름을 확인하지 않도록 설정하는 방법이 있습니다. ReadObject 메서드의 일부 오버로드는 기본적으로 verifyObjectName로 설정된 부울 매개 변수 true 을 가져옵니다. false로 설정하면 래퍼 요소의 이름과 네임스페이스를 무시합니다. 이는 이전에 설명한 단계별 serialization 메커니즘을 사용하여 작성된 XML을 읽을 때 유용합니다.

NetDataContractSerializer 사용

DataContractSerializerNetDataContractSerializer의 주요 차이점은 DataContractSerializer는 데이터 계약 이름을 사용하는 반면 NetDataContractSerializer는 직렬화된 XML의 전체 .NET Framework 어셈블리 및 형식 이름을 출력한다는 것입니다. 즉, 정확히 동일한 형식은 serialization 엔드포인트와 deserialization 엔드포인트 간에 공유되어야 합니다. 다시 말하면 역직렬화할 정확한 형식을 이미 알고 있으므로 알려진 형식 메커니즘은 NetDataContractSerializer 에 필요하지 않습니다.

그러나 여러 가지 문제가 발생할 수 있습니다.

  • 보안. XML에 역직렬화되고 있는 모든 형식이 로드됩니다. 이를 악용해 악의적인 형식이 로드될 수 있습니다. NetDataContractSerializer 속성 또는 생성자 매개 변수로 Serialization 바인더 를 사용하는 경우에만, 신뢰할 수 없는 데이터에 Binder 를 사용해야 합니다. 바인더는 안전한 형식만 로드할 수 있도록 허용합니다. 바인더 메커니즘은 System.Runtime.Serialization 네임스페이스의 형식에서 사용하는 메커니즘과 동일합니다.

  • 버전 관리. XML에 전체 형식 및 어셈블리 이름을 사용하면 형식 버전을 관리할 수 있는 방식이 제한됩니다. 형식 이름, 네임스페이스, 어셈블리 이름 및 어셈블리 버전은 변경할 수 없습니다. AssemblyFormat 속성이나 생성자 매개 변수를 Simple 의 기본값 대신 Full 로 설정하면, 어셈블리 버전을 변경할 수 있지만 일반 매개 변수의 형식 버전은 변경할 수 없습니다.

  • 상호 운용성. .NET Framework 형식 및 어셈블리 이름이 XML에 포함되어 있으므로 .NET Framework 이외의 플랫폼에서는 결과 데이터에 액세스할 수 없습니다.

  • 성능. 형식 및 어셈블리 이름을 작성하면 생성되는 XML의 크기가 크게 늘어납니다.

이 메커니즘은 .NET Framework 원격(특히 BinaryFormatterSoapFormatter)에서 사용하는 이진 파일 또는 SOAP serialization과 유사합니다.

NetDataContractSerializer 사용 방식은 DataContractSerializer사용 방식과 비슷하지만 다음과 같은 차이점이 있습니다.

  • 생성자를 사용하기 위해 루트 형식을 지정하지 않아도 됩니다. NetDataContractSerializer의 같은 인스턴스를 사용하여 형식을 serialize할 수 있습니다.

  • 생성자는 알려진 형식의 목록을 허용하지 않습니다. 형식 이름이 XML로 serialize되는 경우 알려진 형식 메커니즘은 필요하지 않습니다.

  • 생성자는 데이터 계약 서로게이트를 허용하지 않습니다. 대신 ISurrogateSelector 속성에 매핑되는 surrogateSelector 라고 하는 SurrogateSelector 매개 변수를 허용합니다. 이것이 바로 레거시 서로게이트 메커니즘입니다.

  • 생성자는 assemblyFormat 속성에 매핑되는 FormatterAssemblyStyleAssemblyFormat 이라는 매개 변수를 허용합니다. 이전에 설명한 대로 이 메커니즘은 serializer의 버전 관리 기능을 향상시키는 데 사용할 수 있으며, 이진 또는 SOAP serialization의 FormatterAssemblyStyle 메커니즘과 동일합니다.

  • 생성자는 StreamingContext 속성에 매핑되는 context 라는 Context 매개 변수를 허용합니다. 이 메커니즘은 정보를 serialize되고 있는 형식으로 전달할 수 있으며, 이 사용 방법은 다른 StreamingContext 클래스에 사용된 System.Runtime.Serialization 메커니즘의 사용 방법과 동일합니다.

  • SerializeDeserialize 메서드는 WriteObjectReadObject 메서드의 별칭입니다. 이러한 메서드는 이진 또는 SOAP serialization에 보다 일관된 프로그래밍 모델을 제공하기 위해 존재합니다.

이러한 기능에 대한 자세한 내용은 이진 serialization을 참조하세요.

NetDataContractSerializerDataContractSerializer 에서 사용하는 XML 형식은 일반적으로 호환되지 않습니다. 즉, 이러한 직렬 변환기 중 하나로 직렬화하고 다른 직렬 변환기로 역직렬화하도록 시도할 수 없습니다.

또한 NetDataContractSerializer는 개체 그래프의 각 노드에 대한 전체 .NET Framework 형식 및 어셈블리 이름을 출력하지 않습니다. 모호한 정보만을 출력하는데, 즉, 루트 개체 수준에서 다형적 경우에 대해 출력합니다.

참고 항목