Десериализация необязательных полей из BinaryFormatter

У меня есть приложение, которое сериализует данные, используя BinaryFormatter. Член был добавлен в класс, сериализованный из одной версии в другую без изменения имени класса. Был добавлен код для обработки возможного отсутствия добавленного члена в старых сериализованных файлах:

private void readData(FileStream fs, SymmetricAlgorithm dataKey)
{
    CryptoStream cs = null;

    try
    {
        cs = new CryptoStream(fs, dataKey.CreateDecryptor(),
            CryptoStreamMode.Read);
        BinaryFormatter bf = new BinaryFormatter();

        string string1 = (string)bf.Deserialize(cs);
        // do stuff with string1

        bool bool1 = (bool)bf.Deserialize(cs);
        // do stuff with bool1

        ushort ushort1 = (ushort)bf.Deserialize(cs);
        // do stuff with ushort1

        // etc. etc. ...

        // this field was added later, so it may not be present
        // in the serialized binary data.  Check for it, and if
        // it's not there, do some default behavior

        NewStuffIncludedRecently newStuff = null;

        try
        {
            newStuff = (NewStuffIncludedRecently)bf.Deserialize(cs);
        }
        catch
        {
            newStuff = null;
        }

        _newStuff = newStuff != null ?
                new NewStuffIncludedRecently(newStuff) :
                new NewStuffIncludedRecently();
    }
    catch (Exception e)
    {
        // ...
    }
    finally
    {
        // ...
    }
}

Сейчас я нахожусь в том, что мне бы очень хотелось просто промыть и повторить с другим участником, которого я хотел бы добавить, что означало бы, что я бы добавил еще одно поле и блок try-catch, аналогичный тому, что для NewStuffIncludedRecently.

Я думал просто сделать весь класс [Serializable], но не нарушит ли это совместимость со старыми сериализованными данными?

Меня больше всего беспокоит то, что я не понимаю, как работает десериализация. Если я добавлю обработку для другого необязательного поля, как описано выше, будет ли это работать? Какие у меня есть другие варианты, чтобы лучше справиться с этими изменениями?

Заранее спасибо как всегда.


person John    schedule 23.11.2011    source источник
comment
Сериализация поля за полем с помощью бинарного форматирования выглядит как чрезмерное увеличение размера. Вы должны просто использовать бинарную запись, если хотите выполнять сериализацию полей за полем. Я рекомендую сохранить устаревшую схему сериализации для версий старше версии X и передавать любые более новые версии в схему, основанную, например, на DataContracts и/или буферах протоколов.   -  person Anders Forsgren    schedule 23.11.2011
comment
Конечно; BinaryFormatter может быть огромной проблемой при управлении версиями; сериализаторы на основе контрактов, как правило, более дружелюбны (XmlSerializer, DataContractSerializer, protobuf-net и т. д.).   -  person Marc Gravell    schedule 23.11.2011


Ответы (1)


Если вы пометите новые поля [OptionalField], это должно работать, но я слышал сообщения о ненадежности в некоторых случаях. Я не могу сказать наверняка, так как я избегаю BinaryFormatter, потому что у него так много проблем при управлении версиями :) (плюс, он не такой «жесткий», как некоторые альтернативы, и имеет серьезные проблемы, если вы хотите перейти на кроссплатформенный , или в CF/SL и т.д.)

Если вы реализуете ISerializable, вы можете попробовать:

foreach(SerializationEntry entry in info) {
    switch(entry.Name) {
         case "Name": Name = (string)info.Value;
         case "Id": Id = (int)info.Value;
         ...
    }
}

Но опять же, должен подчеркнуть - это усложняет задачу :p При таком подходе вы обрабатываете только те данные, которые на самом деле есть.

person Marc Gravell    schedule 23.11.2011
comment
Спасибо за ваш ответ, Марк (+1). Одна вещь, которая не была слишком ясной в моем вопросе, заключается в том, что сам класс в настоящее время не имеет [Serializable]. Отдельные сериализуемые вещи помечены как [Serializable] (например, NewStuffIncludedRecently), но содержащий класс — нет. Нарушу ли я обратную совместимость, если сделаю содержащий класс [Serializable]? Не добавит ли это что-нибудь к графу объектов? - person John; 23.11.2011
comment
@John, если он реализует ISerializable, то другие атрибуты по существу игнорируются, и вы можете полностью десериализовать его, проверяя его существование. Если это не ISerializable и не [Serializable], это не сработает, насколько я знаю. Вы можете перечислить info, чтобы увидеть, какие поля он содержит; дайте мне знать, если вы хотите пример. - person Marc Gravell; 23.11.2011
comment
@John, я добавил пример SerializationEntry - person Marc Gravell; 23.11.2011