ISerializable и обратная совместимость - PullRequest
6 голосов
/ 10 апреля 2010

Мне нужно работать со старым приложением, которое использовало двоичный формат для сериализации данных приложения в файловый поток (скажем, в файле с именем «data.oldformat») без какой-либо оптимизации основной класс был помечен атрибутом

<serializable()>public MainClass
....... 
end class

и код сериализации

dim b as new binaryformatter
b.serialize(mystream,mymainclass)

В попытке оптимизировать процесс сериализации / десериализации я просто заставил класс реализовать интерфейс ISerializable и написал несколько оптимизированных подпрограмм сериализации

<serializable()>public MainClass
       implements ISerializable
....... 
end class

Оптимизация работает очень хорошо, но я ДОЛЖЕН найти способ перезаписи данных в старых файлах для обратной совместимости.

Как я могу это сделать ??

Пьерлуиджи

Ответы [ 5 ]

4 голосов
/ 28 сентября 2011

stmax имеет отличный ответ, однако я бы реализовал его следующим образом, который использует SerializationEntry.GetEnumerator() вместо try/catch. Этот способ чище и значительно быстрее.

public MainClass(SerializationInfo info, StreamingContext context) {
    int version = 0;
    foreach (SerializationEntry s in info)
    {
        if (s.Name == "version") 
        {
            version = (int)s.Value;
            break;
        }
    }

    switch (version) {
      case 0:
        // deserialize "old format"
        break;
      case 1:
        // deserialize "new format, version 1"
        break;
      default:
        throw new NotSupportedException("version " + version + " is not supported.");
    }
}

Я бы предпочел версию LINQ с использованием .FirstOrDefault (), но SerializationInfo не реализует IEnumerable - на первый взгляд, как ни странно, он даже не реализует старый интерфейс IEnumerable.

4 голосов
/ 10 апреля 2010

, поскольку вы уже реализовали интерфейс ISerializable, вы, вероятно, также уже добавили необходимый конструктор:

public MainClass(SerializationInfo info, StreamingContext context) {}

Вы можете использовать информационный объект, переданный конструктору, для извлечения данных из сериализованного файла. по умолчанию (т.е. когда не реализован ISerializable), имена полей используются как идентификаторы во время сериализации. так что если в вашем старом классе было поле "int x", вы можете десериализовать это, используя: this.x = info.GetInt32("x"); для более новых версий я обычно добавляю запись "версия" во время сериализации, например:

public void GetObjectData(SerializationInfo info, StreamingContext context) {
  info.AddValue("version", 1);
  info.AddValue("othervalues", ...);
}

во время десериализации вы можете проверить запись этой версии и соответственно десериализовать:

public MainClass(SerializationInfo info, StreamingContext context) {
    int version;
    try {
       version = info.GetInt32("version");
    }
    catch {
       version = 0;
    }

    switch (version) {
      case 0:
        // deserialize "old format"
        break;
      case 1:
        // deserialize "new format, version 1"
        break;
      default:
        throw new NotSupportedException("version " + version + " is not supported.");
    }
}

Я не скомпилировал этот код, может содержать опечатки.

надеюсь, что это поможет.

0 голосов
/ 10 апреля 2010

При сериализации ваших объектов добавьте дополнительное поле Версия (это не должно добавить слишком много накладных расходов). Затем в вашем методе GetObjectData попытайтесь сначала извлечь поле версии и на основе того, существует оно или нет (перехватывая исключение SerializationException), десериализовать старый или новый способ. Старый способ просто сериализует все данные, поэтому вы должны просто вызывать Get ... для всех полей.

0 голосов
/ 10 апреля 2010

Ваш предыдущий код должен работать. Вы получаете исключение? Попробуйте использовать новый конструктор:

 Protected Sub New(ByVal info As SerializationInfo, ByVal context As StreamingContext)
0 голосов
/ 10 апреля 2010

Просто попробуйте то же, что вы делали до сих пор

BinaryFormatter b = new BinaryFormatter();
MainClass a = b.DeSerialize(mystream) as MainClass;

Реализация ISerializable не изменила ваш исходный класс, в основном вы просто добавили несколько методов

...