6

que tienen que trabajar un una vieja aplicación que utiliza BinaryFormatter para serializar datos de aplicación en filestream (digamos en un archivo llamado "data.oldformat") sin ningún optimizazion la clase principal se ha marcado con el atributoISerializable y compatibilidad con versiones anteriores

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

y el código de serialización

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

En un intento de optimizar el proceso de serialización/deserialización yo simplemente hice la clase implementa la interfaz ISerializable y escribió algunas seriali optimizado zación rutinas

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

La optimización funciona muy bien, pero tengo que encontrar una manera de reatrive los datos dentro de los viejos archivos de compatibilidad con versiones anteriores.

¿Cómo puedo hacer eso?

Pierluigi

Respuesta

4

stmax tiene una excelente respuesta, sin embargo, me gustaría ponerlo en práctica de este tipo, que utiliza SerializationEntry.GetEnumerator() en lugar de try/catch. De esta manera es más limpio y significativamente más rápido.

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."); 
    } 
} 

yo preferiría una versión de LINQ utilizando .FirstOrDefault(), pero SerializationInfo no implementa IEnumerable - en la cara, extrañamente lo suficiente, ni siquiera poner en práctica la antigua interfaz IEnumerable.

0

Solo trata de lo mismo que has estado haciendo hasta ahora

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

Ejecución ISerializable no cambió su clase original, básicamente, que acaba de añadir algunos métodos

+0

He agregado el constructor requerido (serializationInfo, contexto de streamingContext) así que no puedo usar b.deserialize sin saber cómo la clase principal guardó sus propios datos durante la serialización predeterminada – pierusch

0

Al serializar sus objetos, agregue un campo Versión adicional (esto no debería agregar demasiada sobrecarga). Luego, en su método GetObjectData, trate de recuperar el campo de versión primero y de si existe o no (atrapando la SerializationException) deserializar de la manera antigua o nueva. La forma antigua habrá serializado todos los datos, por lo que debería poder llamar a Obtener ... para todos los campos.

+0

esto está bien para el procedimiento de serialización que usa getObjectData pero el problema es durante el procedimiento de deserialización que utiliza un constructor especializado nuevo (info como serializationinfo, context as serializationcontext) la versión anterior de mainClass no implementó ISerializable así que no sé cómo recuperar datos utilizando la información de transmisión objeto – pierusch

0

Su código anterior debería funcionar. ¿Recibes una excepción? Trate de usar constructor new:

Protected Sub New(ByVal info As SerializationInfo, ByVal context As StreamingContext) 
+0

tuve ... pero requiere saber cómo el binaryformatter guarda datos cuando la clase solo está marcada como serializable ... – pierusch

4

puesto que ya ha implementado la interfaz ISerializable, probablemente también ya ha añadido el constructor requerido:

public MainClass(SerializationInfo info, StreamingContext context) {} 

puede utilizar la información-objeto pasado al constructor para recuperar datos del archivo serializado. de forma predeterminada (es decir, cuando no se implementa ISerializable), los nombres de los campos se utilizan como identificadores durante la serialización. así que si su antigua clase tenía un campo "int x" puede deserializar esta usando:

this.x = info.GetInt32("x"); 

si hay nuevas versiones que normalmente agregar una entrada de "versión" durante la serialización, así:

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

durante la deserialización puede comprobar la entrada de versión y deserializar en consecuencia:

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."); 
    } 
} 

no he compilado el código, puede contener errores tipográficos.

espero que ayude.

+0

great answer stmax! ¿Cómo encontraste el comportamiento predeterminado para el objeto binaryformatter? – pierusch

+3

hice lo siguiente para encontrar los "nombres" de las entradas que se serializaron con el comportamiento predeterminado: foreach (entrada SerializationEntry en info.GetEnumerator()) {Trace.WriteLine (entry.Name); } – stmax

+0

En lugar de try/catch al obtener "versión", en su lugar use SerializationInfo.GetEnumerator() y busque el campo "versión". Es más limpio y si no se encuentra el campo, entonces evitar lanzar la excepción hace que la deserialización completa (en mi prueba) sea 2 veces más rápida. SerializationInfo.Get() utiliza internamente la misma búsqueda lineal que hacerlo usted mismo con un enumerador, lo que hace que ambos sean O (n) en el número de campos. –

Cuestiones relacionadas