Десериализация NetDataContractSerializer с новым свойством
Не имея реального предвидения, я сериализовал большой набор данных, оформленный только с помощью Serializable, используя NetDataContractSerializer, и теперь я хотел бы добавить новое поле. Какие у меня варианты?
Исходный класс выглядит примерно так (с несколькими уровнями наследования и довольно большим количеством полей):
[Serializable]
public class InheritedClass : BaseClass
{
public string StringId { get; set; }
}
А теперь я хотел бы добавить еще одно свойство, скажем что-то вроде:
[Serializable]
public class InheritedClass : BaseClass
{
public string StringId { get; set; }
public int IntId { get; set; }
}
Теперь, когда я обновляю класс и перехожу на десериализацию, я получаю исключение, так как нового поля нет, что-то вроде:
Exception thrown: 'System.Runtime.Serialization.SerializationException' in System.Runtime.Serialization.dll
Additional information: Error in line 1 position 601. 'Element' '_x003C_StringId_x003E_k__BackingField' from namespace 'http://schemas.datacontract.org/2004/07/QT' is not expected. Expecting element '_x003C_IntId_x003E_k__BackingField'.
Итак, это имеет смысл, поскольку NetDataContractSerializer требует того же класса. Я могу обойти это, используя атрибут DataMember, например:
[DataMember(IsRequired = false)]
Проблема в том, что переключение на DataMember (как я должен был сделать заранее или использовать другой сериализатор) изменяет неявный алфавитный порядок, и тогда большинство моих полей молча не десериализуются, как хорошо известно.
Я попытался добавить порядок, который соответствует порядку на диске вручную (через Order
свойства атрибута), но это тоже не соблюдается. (Я не вижу значения заказа, которое я мог бы найти в необработанном xml).
Есть ли другие варианты помимо написания чего-либо для загрузки XML и вставки отсутствующего узла? (Или эквивалентно настроить параллельный тип и десериализовать из одного повторно сериализовать в другой?) Если нет, я, вероятно, просто загружу текущий тип и десериализовать в JsonNet или protobuf, но я упускаю что-то более простое с DataMember/ так далее?
1 ответ
Маркировка типа с помощью [Serializable]
означает, что тип должен быть сериализован путем сериализации его открытых и закрытых полей, а не его свойств. При добавлении нового поля обычно обрабатывают устаревшие данные, чтобы пометить его как [OptionalField]
чтобы указать, что он не всегда будет присутствовать в потоках сериализации - но это невозможно сделать с помощью автоматически реализуемого свойства, поскольку невозможно получить доступ к секретному полю поддержки.
Решение состоит в том, чтобы явно реализовать все новые свойства и пометить вспомогательное поле обязательным атрибутом:
[Serializable]
public class InheritedClass : BaseClass
{
public string StringId { get; set; }
[OptionalField]
int intId;
public int IntId { get { return intId; } set { intId = value; } }
}