Я пытаюсь поддерживать передовую совместимость для сериализатора контрактных данных. У меня есть проблемы с:Serializer данных Контракт Совместимость свойств, на которые ссылаются
Если у вас есть объект, который сохраняется как ссылка на свойство, которое добавлено в более позднюю версию внутри известного типа, оно станет исключением. Обратите внимание, что оба этих типа известны в обеих версиях. Единственное, что является новым, это свойство внутри одного из объектов.
Я приложил простое моделирование задачи на samples и samples:
Она состоит из двух различных проектов: V1, который является более старой версией, которая уже развернутой. V2, который является более новой версией V1. V2 сохраняет свои данные, а V1 должен иметь возможность загружать сохраненные данные V2 для поддержки прямой совместимости.
Существует три пользовательских типа: Люди: имеет две ссылки на объекты, а Person и AnotherPerson сохраняются в них.
В V1 и V2:
[DataContract(Name = "People", Namespace = "Tests.FCTests")]
[KnownType(typeof(Person))]
[KnownType(typeof(AnotherPerson))]
public class People : IExtensibleDataObject
{
[DataMember]
public object Person { get; set; }
[DataMember]
public object AnotherPerson { get; set; }
public ExtensionDataObject ExtensionData { get; set; }
}
лицо: имеет имя.
В V1 и V2:
[DataContract(Name = "Person", Namespace = "Tests.FCTests")]
public class Person : IExtensibleDataObject
{
[DataMember]
public string Name { get; set; }
public ExtensionDataObject ExtensionData { get; set; }
}
AnotherPerson: имеет имя и в V2 была добавлена ссылка на Человека (FriendPerson).
В V1:
[DataContract(Name = "AnotherPerson", Namespace = "Tests.FCTests")]
public class AnotherPerson : IExtensibleDataObject
{
[DataMember]
public string Name { get; set; }
public ExtensionDataObject ExtensionData { get; set; }
}
в V2:
[DataContract(Name = "AnotherPerson", Namespace = "Tests.FCTests")]
public class AnotherPerson : IExtensibleDataObject
{
[DataMember]
public string Name { get; set; }
/* This is added in this version */
[DataMember]
public Person FriendPerson { get; set; }
public ExtensionDataObject ExtensionData { get; set; }
}
Вариант 2 сохраняет данные:
static void Main(string[] args)
{
DataContractSerializer serializer = new DataContractSerializer(typeof(People), null, int.MaxValue, false, true, null, null);
var people = new People();
var person = new Person() { Name = "Person" };
var anotherPerson = new AnotherPerson() { Name = "AnotherPerson", FriendPerson = person };
people.Person = person;
people.AnotherPerson = anotherPerson;
using (var writer = new XmlTextWriter("../../../../SavedFiles/Version2Saved.xml", null) { Formatting = Formatting.Indented })
{
serializer.WriteObject(writer, people);
writer.Flush();
}
Console.WriteLine("Save Successfull.");
Console.ReadKey();
}
Version 1 загружает те же данные:
static void Main(string[] args)
{
DataContractSerializer serializer = new DataContractSerializer(typeof(People), null, int.MaxValue, false, true, null, null);
People loadedPeople;
using (var reader = new XmlTextReader("../../../../SavedFiles/Version2Saved.xml"))
{
loadedPeople = (People)serializer.ReadObject(reader);
}
Console.WriteLine("Load Successful.");
Console.ReadKey();
}
Сохраненные данные:
<People xmlns:i="http://www.w3.org/2001/XMLSchema-instance" z:Id="1" xmlns:z="http://schemas.microsoft.com/2003/10/Serialization/" xmlns="Tests.FCTests">
<AnotherPerson z:Id="2" i:type="AnotherPerson">
<FriendPerson z:Id="3">
<Name z:Id="4">Person</Name>
</FriendPerson>
<Name z:Id="5">AnotherPerson</Name>
</AnotherPerson>
<Person z:Ref="3" i:nil="true" />
</People>
Когда V1 пытается загрузить данные этого генерируется исключение:
{System.Runtime.Serialization.SerializationException: Element Person from namespace Tests.FCTests cannot have child contents to be deserialized as an object. Please use XmlNode[] to deserialize this pattern of XML. ---> System.Xml.XmlException: 'Element' is an invalid XmlNodeType.
at System.Xml.XmlReader.ReadEndElement()
at System.Runtime.Serialization.XmlReaderDelegator.ReadEndElement()
at System.Runtime.Serialization.ObjectDataContract.ReadXmlValue(XmlReaderDelegator reader, XmlObjectSerializerReadContext context)
--- End of inner exception stack trace ---
at System.Runtime.Serialization.ObjectDataContract.ReadXmlValue(XmlReaderDelegator reader, XmlObjectSerializerReadContext context)
at System.Runtime.Serialization.XmlObjectSerializerReadContext.ReadDataContractValue(DataContract dataContract, XmlReaderDelegator reader)
at System.Runtime.Serialization.XmlObjectSerializerReadContext.InternalDeserialize(XmlReaderDelegator reader, String name, String ns, Type declaredType, DataContract& dataContract)
at System.Runtime.Serialization.XmlObjectSerializerReadContextComplex.InternalDeserialize(XmlReaderDelegator xmlReader, Type declaredType, String name, String ns)
at System.Runtime.Serialization.XmlObjectSerializerReadContext.DeserializeFromExtensionData(IDataNode dataNode, Type type, String name, String ns)
at System.Runtime.Serialization.XmlObjectSerializerReadContext.GetExistingObject(String id, Type type, String name, String ns)
at System.Runtime.Serialization.XmlObjectSerializerReadContext.TryHandleNullOrRef(XmlReaderDelegator reader, Type declaredType, String name, String ns, Object& retObj)
at System.Runtime.Serialization.XmlObjectSerializerReadContext.InternalDeserialize(XmlReaderDelegator reader, String name, String ns, Type declaredType, DataContract& dataContract)
at System.Runtime.Serialization.XmlObjectSerializerReadContextComplex.InternalDeserialize(XmlReaderDelegator xmlReader, Int32 declaredTypeID, RuntimeTypeHandle declaredTypeHandle, String name, String ns)
at ReadPeopleFromXml(XmlReaderDelegator , XmlObjectSerializerReadContext , XmlDictionaryString[] , XmlDictionaryString[])
at System.Runtime.Serialization.ClassDataContract.ReadXmlValue(XmlReaderDelegator xmlReader, XmlObjectSerializerReadContext context)
at System.Runtime.Serialization.XmlObjectSerializerReadContext.ReadDataContractValue(DataContract dataContract, XmlReaderDelegator reader)
at System.Runtime.Serialization.XmlObjectSerializerReadContext.InternalDeserialize(XmlReaderDelegator reader, String name, String ns, Type declaredType, DataContract& dataContract)
at System.Runtime.Serialization.XmlObjectSerializerReadContextComplex.InternalDeserialize(XmlReaderDelegator xmlReader, Type declaredType, DataContract dataContract, String name, String ns)
at System.Runtime.Serialization.DataContractSerializer.InternalReadObject(XmlReaderDelegator xmlReader, Boolean verifyObjectName, DataContractResolver dataContractResolver)
at System.Runtime.Serialization.XmlObjectSerializer.ReadObjectHandleExceptions(XmlReaderDelegator reader, Boolean verifyObjectName, DataContractResolver dataContractResolver)
at System.Runtime.Serialization.DataContractSerializer.ReadObject(XmlReader reader)
at Version1.Program.Main(String[] args) in C:\Users\Administrator\Desktop\Unknown types Test\Version1\Version1\Program.cs:line 17}
Внутренняя Исключение:
{System.Xml.XmlException: 'Element' is an invalid XmlNodeType.
at System.Xml.XmlReader.ReadEndElement()
at System.Runtime.Serialization.XmlReaderDelegator.ReadEndElement()
at System.Runtime.Serialization.ObjectDataContract.ReadXmlValue(XmlReaderDelegator reader, XmlObjectSerializerReadContext context)}
Я подозреваю, что ошибка происходит потому, что объект ссылается на тип, который десериализуется внутри объекта расширения и не имеет какого-либо типа. Причина в том, что если вы добавили новый экземпляр Person внутри People и не ссылаетесь на тот же экземпляр внутри AnotherPerson (FriendPerson).
var anotherPerson = new AnotherPerson() { Name = "AnotherPerson", FriendPerson = new Person() };
Затем сохраненный файл будет следующим, и все работает просто отлично:
<People xmlns:i="http://www.w3.org/2001/XMLSchema-instance" z:Id="1" xmlns:z="http://schemas.microsoft.com/2003/10/Serialization/" xmlns="Tests.FCTests">
<AnotherPerson z:Id="2" i:type="AnotherPerson">
<FriendPerson z:Id="3">
<Name i:nil="true" />
</FriendPerson>
<Name z:Id="4">AnotherPerson</Name>
</AnotherPerson>
<Person z:Id="5" i:type="Person">
<Name z:Id="6">Person</Name>
</Person>
</People>
Я попытался решить эту проблему с помощью контракта данных Разрешитель, Добавление известных типов внутри сериализатором динамически и контракт данных Суррогат, но никто из них не работал. Причина в том, что исключение возникает, когда сериализатор де-сериализует FriendPerson, и переопределенные методы внутри суррогата или резольвера до этого не вызываются.
ПРИМЕЧАНИЕ Нам нужно сохранить ссылки на объекты, и удаление их не является вариантом.
Это решит эту проблему, но на самом деле это не общее решение. Что делать, если в V2 Person также добавляется ссылка на AnotherPerson? Тогда использование заказа не может решить проблему. – Arash