Using the XmlSerializer Class
Windows Communication Foundation (WCF) can use two different serialization technologies to turn the data in your application into XML that is transmitted between clients and services, a process called serialization.
DataContractSerializer as the Default
By default WCF uses the DataContractSerializer class to serialize data types. This serializer supports the following types:
Primitive types (for example, integers, strings, and byte arrays), as well as some special types, such as XmlElement and DateTime, which are treated as primitives.
Data contract types (types marked with the DataContractAttribute attribute).
Types marked with the SerializableAttribute attribute, which include types that implement the ISerializable interface.
Types that implement the IXmlSerializable interface.
Many common collection types, which include many generic collection types.
Many .NET Framework types fall into the latter two categories and are thus serializable. Arrays of serializable types are also serializable. For a complete list, see Specifying Data Transfer in Service Contracts.
The DataContractSerializer, used together with data contract types, is the recommended way to write new WCF services. For more information, see Using Data Contracts.
When to Use the XmlSerializer Class
WCF also supports the XmlSerializer class. The XmlSerializer class is not unique to WCF. It is the same serialization engine that ASP.NET Web services use. The XmlSerializer class supports a much narrower set of types than the DataContractSerializer class, but allows much more control over the resulting XML and supports much more of the XML Schema definition language (XSD) standard. It also does not require any declarative attributes on the serializable types. For more information, see the XML Serialization topic in the .NET Framework documentation. The XmlSerializer class does not support data contract types.
When using Svcutil.exe or the Add Service Reference feature in Visual Studio to generate client code for a third-party service, or to access a third-party schema, an appropriate serializer is automatically selected for you. If the schema is not compatible with the DataContractSerializer, the XmlSerializer is selected.
Manually Switching to the XmlSerializer
At times, you may have to manually switch to the XmlSerializer. This happens, for example, in the following cases:
When migrating an application from ASP.NET Web services to WCF, you may want to reuse existing, XmlSerializer-compatible types instead of creating new data contract types.
When precise control over the XML that appears in messages is important, but a Web Services Description Language (WSDL) document is not available, for example, when creating a service with types that have to comply to a certain standardized, published schema that is not compatible with the DataContractSerializer.
When creating services that follow the legacy SOAP Encoding standard.
In these and other cases, you can manually switch to the XmlSerializer class by applying the XmlSerializerFormatAttribute attribute to your service, as shown in the following code.
<ServiceContract(), XmlSerializerFormat()> _
Public Class BankingService
<OperationContract()> _
Public Sub ProcessTransaction(ByVal bt As BankingTransaction)
' Code not shown.
End Sub
End Class
' BankingTransaction is not a data contract class,
' but is an XmlSerializer-compatible class instead.
Public Class BankingTransaction
<XmlAttribute()> _
Public Operation As String
<XmlElement()> _
Public fromAccount As Account
<XmlElement()> _
Public toAccount As Account
<XmlElement()> _
Public amount As Integer
End Class
'Notice that the Account class must also be XmlSerializer-compatible.
[ServiceContract]
[XmlSerializerFormat]
public class BankingService
{
[OperationContract]
public void ProcessTransaction(BankingTransaction bt)
{
// Code not shown.
}
}
//BankingTransaction is not a data contract class,
//but is an XmlSerializer-compatible class instead.
public class BankingTransaction
{
[XmlAttribute]
public string Operation;
[XmlElement]
public Account fromAccount;
[XmlElement]
public Account toAccount;
[XmlElement]
public int amount;
}
//Notice that the Account class must also be XmlSerializer-compatible.
Security Considerations
Note
It is important to be careful when switching serialization engines. The same type can serialize to XML differently depending on the serializer being used. If you accidentally use the wrong serializer, you might be disclosing information from the type that you did not intend to disclose.
For example, the DataContractSerializer class only serializes members marked with the DataMemberAttribute attribute when serializing data contract types. The XmlSerializer class serializes any public member. See the type in the following code.
<DataContract()> _
Public Class Customer
<DataMember()> _
Public firstName As String
<DataMember()> _
Public lastName As String
Public creditCardNumber As String
End Class
[DataContract]
public class Customer
{
[DataMember]
public string firstName;
[DataMember]
public string lastName;
public string creditCardNumber;
}
If the type is inadvertently used in a service contract where the XmlSerializer class is selected, the creditCardNumber
member is serialized, which is probably not intended.
Even though the DataContractSerializer class is the default, you can explicitly select it for your service (although doing this should never be required) by applying the DataContractFormatAttribute attribute to the service contract type.
The serializer used for the service is an integral part of the contract and cannot be changed by selecting a different binding or by changing other configuration settings.
Other important security considerations apply to the XmlSerializer class. First, it is strongly recommended that any WCF application that uses the XmlSerializer class is signed with a key that is safeguarded from disclosure. This recommendation applies both when a manual switch to the XmlSerializer is performed and when an automatic switch is performed (by Svcutil.exe, Add Service Reference, or a similar tool). This is because the XmlSerializer serialization engine supports the loading of pre-generated serialization assemblies as long as they are signed with the same key as the application. An unsigned application is completely unprotected from the possibility of a malicious assembly matching the expected name of the pre-generated serialization assembly being placed in the application folder or the global assembly cache. Of course, an attacker must first gain write access to one of these two locations to attempt this action.
Another threat that exists whenever you use XmlSerializer is related to write access to the system temporary folder. The XmlSerializer serialization engine creates and uses temporary serialization assemblies in this folder. You should be aware that any process with write access to the temporary folder may overwrite these serialization assemblies with malicious code.
Rules for XmlSerializer support
You cannot directly apply XmlSerializer-compatible attributes to contract operation parameters or return values. However, they can be applied to typed messages (message contract body parts), as shown in the following code.
<ServiceContract(), XmlSerializerFormat()> _
Public Class BankingService
<OperationContract()> _
Public Sub ProcessTransaction(ByVal bt As BankingTransaction)
'Code not shown.
End Sub
End Class
<MessageContract()> _
Public Class BankingTransaction
<MessageHeader()> _
Public Operation As String
<XmlElement(), MessageBodyMember()> _
Public fromAccount As Account
<XmlElement(), MessageBodyMember()> _
Public toAccount As Account
<XmlAttribute(), MessageBodyMember()> _
Public amount As Integer
End Class
[ServiceContract]
[XmlSerializerFormat]
public class BankingService
{
[OperationContract]
public void ProcessTransaction(BankingTransaction bt)
{
//Code not shown.
}
}
[MessageContract]
public class BankingTransaction
{
[MessageHeader]
public string Operation;
[XmlElement, MessageBodyMember]
public Account fromAccount;
[XmlElement, MessageBodyMember]
public Account toAccount;
[XmlAttribute, MessageBodyMember]
public int amount;
}
When applied to typed message members, these attributes override properties that conflict on the typed message attributes. For example, in the following code, ElementName
overrides Name
.
<MessageContract()> _
Public Class BankingTransaction
<MessageHeader()> _
Public Operation As String
'This element will be <fromAcct> and not <from>:
<XmlElement(ElementName := "fromAcct"), _
MessageBodyMember(Name := "from")> _
Public fromAccount As Account
<XmlElement(), MessageBodyMember()> _
Public toAccount As Account
<XmlAttribute(), MessageBodyMember()> _
Public amount As Integer
End Class
[MessageContract]
public class BankingTransaction
{
[MessageHeader] public string Operation;
//This element will be <fromAcct> and not <from>:
[XmlElement(ElementName="fromAcct"), MessageBodyMember(Name="from")]
public Account fromAccount;
[XmlElement, MessageBodyMember]
public Account toAccount;
[XmlAttribute, MessageBodyMember]
public int amount;
}
The MessageHeaderArrayAttribute attribute is not supported when using the XmlSerializer.
Note
In this case, the XmlSerializer throws the following exception, which is released prior to WCF: "An element declared at the top level of a schema cannot have maxOccurs > 1. Provide a wrapper element for 'more' by using XmlArray or XmlArrayItem instead of XmlElementAttribute, or by using the Wrapped parameter style."
If you receive such an exception, investigate whether this situation applies.WCF does not support the SoapIncludeAttribute and XmlIncludeAttribute attributes in message contracts and operation contracts; use the KnownTypeAttribute attribute instead.
See Also
Tasks
How to: Improve the Startup Time of WCF Client Applications using the XmlSerializer
Reference
DataContractFormatAttribute
DataContractSerializer
XmlSerializer
MessageHeaderArrayAttribute
Concepts
Specifying Data Transfer in Service Contracts
Using Data Contracts