2011-03-21 16 views
15

Estoy tratando de deserializar "SomeClass" con una versión anterior de una aplicación. Me sale este continuación excepciónDeserialización compatibilidad con versiones anteriores

System.Runtime.Serialization.SerializationException: El ObjectManager encontró un número no válido de fixups. Esto generalmente indica un problema en el formateador.

deserialización tiros excepción cuando serializar versión 0.9 y trato de deserializar utilizando la versión 0.8. Pensé que el atributo OptionalField haría el truco, pero no fue así.

// Version 0.8 
[Serializable()] 
class Foo{ 
    Bar b; 
} 

// Version 0.9 
[Serializable()] 
class Foo{ 
    Bar b; 
    [OptionalField] 
    Zoo z; 
} 

Dado que no puedo cambiar la versión 0.8, ¿cómo debo añadir más estado en Foo objeto de tal manera que las versiones anteriores pueden deserializar todo lo posible?

Cualquier apuntador será muy apreciado.

Actualización 1 Bar y Zoo son otras clases que son serializables y contienen Hashtables y otras cosas serializables. Todo es serializable en esas clases. Además, no tengo puntales.

+0

Si el campo es opcional o no es irrelevante aquí, el hecho es que la serialización no funciona en todas las versiones, AFAIK; eso no quiere decir que no se pueden agregar miembros, por supuesto que se puede, pero, por ejemplo, no puedo simplemente definir un tipo literal y deserializar los artículos serializados con los existentes, algo que tenga que ver con los ensamblajes o los tipos 'token ', Creo. –

+0

kareph, ¿cuál es el tipo real de 'Zoo'? Recuerdo que algunos tipos (matrices) simplemente no funcionaban bien. –

+0

¿Estaría dispuesto a usar la serialización xml en lugar de la serialización binaria? Eso sería más seguro para la versión. – code4life

Respuesta

26

En primer lugar, nunca, nunca utilizar las funciones de serialización del CLR para cualquier cosa que se asemeja a largo plazo almacenamiento. Cometimos ese error generalmente una vez, colocamos objetos en un campo de base de datos de blobs y nos damos palmadas en la espalda pensando que somos inteligentes. Y luego, el CLR recibe un parche o nuestros ensamblajes cambian de versión y estás jodido. Entonces no lo hagas

Si aún desea hacerlo, la mejor manera de manejar el problema es crear su propia SerializationBinder que se ve algo como esto:

public sealed class CustomBinder : SerializationBinder { 

    public override Type BindToType(string assemblyName, string typeName) { 

     Type typeToDeserialize = null; 

     if (typeName.IndexOf("SomeType") != -1) { 
      typeToDeserialize = typeof(Foo.Bar.Bax.NewType); 
     } 
     else if (typeName.IndexOf("SomeOtherType") != -1) { 
      typeToDeserialize = typeof(Foo.Bar.Bax.SomeOtherNewType); 
     } 
     else { 
      // ... etc 
     } 

     return typeToDeserialize; 
    } 
} 

Establecer la propiedad Binder del formateador está utilizando antes deserializar para que anule los valores predeterminados.

Tenga en cuenta que no estoy ofreciendo una solución de acceso directo aquí, estoy recomendando cómo resolver el problema. Una vez que hayas convertido todo lo que estás haciendo, investiga otras tecnologías de serialización como protobuf o escribe la tuya. De cualquier manera, nunca debe confiar en CLR para obtener soporte de serialización a largo plazo.

+2

¿su comentario 'NUNCA' también se aplica a la serialización de XML? Me parece que con DataContract y DataMember hay un buen control sobre la estructura generada. Estaría interesado en obtener su opinión al respecto. – paul

+0

No, pero el serializador binario no es adecuado para la serialización a largo plazo. Es el más adecuado para el bombeo de bytes a través del cable, por ejemplo, en una aplicación cliente-servidor. WebForms por ejemplo lo usa. – Sebazzz

2

Parece que una forma de hacerlo sería tener un objeto versionado, de esa manera podría intentar deserializar el objeto utilizando la última versión. Si eso no funcionó, retroceda una versión hasta que tenga éxito. Luego, una vez que tenga su objeto, actualícelo a la última versión del objeto y use los valores predeterminados para cualquier campo para el que no tenga datos.

+0

Si degradamos (resultado de la reversión) nuestra aplicación, entonces tendremos campos que faltan (en lugar de datos faltantes); de todos modos lo que dices es lo que debería ser. – karephul

+0

Sé que eso es lo que los programadores de C usan para hacer. Pensé que estamos programando a un nivel muy alto de abstracción y esto podría ser un problema estándar. Gracias por tus ideas. – karephul

0

El atributo optional field debería haberlo hecho. ¿Puedes publicar las clases reales que intentas serializar?

Usted podría tratar de estas cosas primero -

convertir structs si alguno de classes

tratar Soap Serialization en lugar de binary serilization

+0

He actualizado la publicación. – karephul

+0

¿Has probado la serialización de jabón? –

4

Si los constructores de cada versión son compatibles (p.hay un sin parámetros o Foo(Bar b) constructor para ambas versiones) se puede llamar

BinaryFormatter formatter = new BinaryFormatter(); 
formatter.AssemblyFormat = Formatters.FormatterAssemblyStyle.Simple; 

Antes de deserializar su flujo.

3

Como advertencia para las personas que investigan este problema "antes de que sea demasiado tarde" ... Recomiendo encarecidamente que no continúen a través de BinaryFormatter. Está bien para transferencias transitorias entre 2 aplicaciones-dominios que están sincronizados, pero eso es IMO. Existen otras herramientas de serialización que no tienen estos problemas. En términos de binario, protobuf-net es una opción bastante razonable, que permite añadir/eliminar/cambiar el nombre, etc., sin ningún problema.

Cuestiones relacionadas