WCF is used to transfer data between clients and a server. Old DTO:
[Serializable]
public class TestClass
{
private string firstProperty;
[XmlAttribute]
public string FirstProperty
{
get => firstProperty;
set => firstProperty = value;
}
}
Clients will keep sending the old version. The class needs to be extended to look like this:
[Serializable]
public class TestClass
{
private string firstProperty;
private string secondProperty;
[XmlAttribute]
public string FirstProperty
{
get => firstProperty;
set => firstProperty = value;
}
[XmlAttribute]
public string SecondProperty
{
get => secondProperty;
set => secondProperty = value;
}
}
Serialization:
public static void SerializeDataContract<T>(T obj, string path)
{
var serializer = new DataContractSerializer(typeof(T));
var settings = new XmlWriterSettings { Indent = true };
using (var writer = XmlWriter.Create(path, settings))
{
serializer.WriteObject(writer, obj);
}
}
public static T DeserializeDataContract<T>(string path)
{
var serializer = new DataContractSerializer(typeof(T));
using (var s = File.OpenRead(path))
{
return (T) serializer.ReadObject(s);
}
}
The server and some clients will use the new version. If I serialize the old version and deserialize it into the new version, the following error is thrown:
System.Runtime.Serialization.SerializationException: 'Error in line 1 position 129. 'EndElement' 'TestClass' from namespace 'http://schemas.datacontract.org/2004/07/DTOs' is not expected. Expecting element 'secondProperty'.'
The exception is usually thrown in the WCF layer but I extracted a minimal reproducible example. If I use the XmlSerializer the error is gone. But changing the serializer is not an option because the old clients will keep using the DataContractSerializer.
So I'm having a hard time getting this to work because of the XmlSerializer attributes in combination with the DataContractSerializer. Any advice?
CodePudding user response:
You can indicate that secondProperty
is optional by marking it with [OptionalFieldAttribute]
:
[Serializable]
public class TestClass
{
private string firstProperty;
[OptionalField]
private string secondProperty;
[XmlAttribute]
public string FirstProperty
{
get => firstProperty;
set => firstProperty = value;
}
[XmlAttribute]
public string SecondProperty
{
get => secondProperty;
set => secondProperty = value;
}
}
When a type is marked with [Serializable]
but not data contract attributes, the data contract serializer will serialize the public and private fields of instances of the type -- not the properties -- in a manner similar to BinaryFormatter
. As such all fields are required to be present unless marked with [OptionalField]
.
For more see Types Supported by the Data Contract Serializer and Version tolerant serialization: Tolerance of missing data.
Notes:
To mark the backing field of an auto-implemented property as optional, see NetDataContractSerializer Deserialization With New Property.
That being said, I don't recommend using auto-properties with
[Serializable]
types since the serialization stream will contain the names of the secret backing fields. See .NET WebAPI Serialization k_BackingField Nastiness for details.You have marked your type with
[XmlAttribute]
, but this attribute is ignored byDataContractSerializer
. It only affects serialization byXmlSerializer
.
Demo fiddle here.