2011-08-16 9 views
5

Tengo una situación en la que estoy serializando algunos objetos .NET usando NetDataContractSerializer y almacenando el XML en una base de datos para recordar el estado de estos objetos dentro de una aplicación. Recientemente, me encontré con la primera situación en la que la refactorización de códigos de propiedades y nombres de tipos ha provocado que no se deserialicen estos datos XML.Problema de deserialización con NetDataContractSerializer después de refactorizar el código

Hasta ahora he presentado dos planes de ataque diferentes para saber cómo lidiar con los saltos de compatibilidad de versiones como estos, que deben usar los recursos disponibles dentro del NetDataContractSerializer para controlar la deserialización o simplemente para transformar el XML directamente. De mi experimentación e investigación parece que uno puede deserializar a un tipo diferente usando un custom SerializationBinder y los cambios de nombre/tipo de propiedad pueden abordarse implementando ISerializable o escribiendo un sustituto de serialización implementando ISurrogateSelector y ISerializationSurrogate. Lamentablemente, este mecanismo preferido no se ha procesado y, a menos que se me muestre lo contrario, parece que usar sustitutos mover entre la versión de los datos serializados no es posible con el NetDataContractSerializer y esto se debe a un inexplicable design decision by Microsoft. Lo que Microsoft ha sugerido es usar la misma serialización en ambos lados, lo que frustra por completo el uso de un sustituto para ayudar en los casos en que un nombre de tipo cambia o se mueve a un espacio de nombres o ensamblaje diferente.

Para solucionarlo, por favor utilice la misma instancia NetDataContractSerializer o otra instancia que también se inicializa con un compatibles SurrogateSelector.

Esta explicación conflictos con an MSDN article que tiene esto que decir acerca del uso de una carpeta de encargo para sustituir tipos junto con el trato con otros cambios en la estructura de serialización.

Durante la deserialización, el formateador ve que se ha configurado una carpeta. Como cada objeto está a punto de deserializarse, el formateador llama al método BindToType del enlazador , pasándole el nombre del ensamblado y el tipo que el formateador desea deserializar. En este punto, BindToType decide qué tipo debe realmente construirse y devuelve este tipo.

Tenga en cuenta que el tipo original y el nuevo tipo deben tener el mismo campo exacto nombres y tipos si el nuevo tipo utiliza serialización simple a través del atributo personalizado Serializable . Sin embargo, la nueva versión del tipo podría implementar la interfaz ISerializable y luego se llamará a su constructor especial y el tipo puede examinar los valores en el objeto SerializationInfo y determinar cómo deserializarse.

Así que o bien podré hacer que NetDataContractSerializer funcione para deserializar mi V1 XML en mis tipos de V2 o tendré que transformar manualmente el XML. Si alguien pudiera probar que SerializationInfo de NetDataContractSerializer funciona de hecho al usar ISerializable o utilizar sustitutos de serialización que serían excelentes o al menos dar una explicación mejor que la dada por Microsoft, de lo contrario, probablemente publicaré una nueva pregunta para debatir de la mejor manera en .NET para transformar el viejo XML directamente.

ACTUALIZACIÓN 2011-08-16: Después de algunos experimentos parece que el ISerializable y la técnica de serialización sustituta tanto funcionan bien si el tipo de original que fue serializado implementado ISerializable de otro modo si el tipo sólo se utiliza la [Serializable] atribuirlo parece que a cada campo en el gráfico de objetos le falta información de tipo valioso en forma de atributos adicionales.

Ejemplo utilizando [Serializable] atributo

<OldClass2 xmlns:i="http://www.w3.org/2001/XMLSchema-instance" z:Id="1" z:Type="NdcsSurrogateTest.OldClass2" z:Assembly="NdcsSurrogateTest, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" xmlns:z="http://schemas.microsoft.com/2003/10/Serialization/" xmlns="http://schemas.datacontract.org/2004/07/NdcsSurrogateTest"> 
    <_stable z:Id="2">Remains the same</_stable> 
    <_x003C_OldProperty_x003E_k__BackingField>23</_x003C_OldProperty_x003E_k__BackingField> 
</OldClass2> 

Ejemplo implementar ISerialzable:

<OldClass2 xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns:x="http://www.w3.org/2001/XMLSchema" z:Id="1" z:Type="NdcsSurrogateTest.OldClass2" z:Assembly="NdcsSurrogateTest, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" xmlns:z="http://schemas.microsoft.com/2003/10/Serialization/" xmlns="http://schemas.datacontract.org/2004/07/NdcsSurrogateTest"> 
    <_stable z:Id="2" z:Type="System.String" z:Assembly="0" xmlns="">Remains the same</_stable> 
    <_x003C_OldProperty_x003E_k__BackingField z:Id="3" z:Type="System.Int32" z:Assembly="0" xmlns="">23</_x003C_OldProperty_x003E_k__BackingField> 
</OldClass2> 

Cuando deserializar el primer ejemplo usando el NetDataContractSerializer con una carpeta de encargo para cambiar el tipo y luego ya sea implementando ISerializable en ese tipo o proporcionando un selector sustituto que designa un sustituto de serialización que es básico cumple con la función ISerializalbe, verá una SerializationInfo vacía en el método ISerializationSurrogate.SetObjectData. Al procesar el xml en el segundo ejemplo, SerializationInfo parece obtener la información correcta y las cosas funcionan como se esperaba.

Mi conclusión es que el XML predeterminado producido por NetDataContractSerializer para los tipos que admiten la serialización solo a través de SerializableAttribute no será compatible con la deserialización utilizando ISerializable o técnicas de sustitución de serialización debido a la falta de información de tipo. Por lo tanto, para hacer que el uso de NetDataContractSerializable sea más a prueba de futuro, se debe personalizar la serialización para garantizar que esta información de tipo se incluya en XML para que la deserialización posterior se pueda personalizar sin tener que transformar manualmente el código fuente XML.

Respuesta

2

Sí, tiene que tener una ruta de migración de datos bien pensada si está utilizando la serialización. La solución que mencionó es lo que haría personalmente, incluir un cheque en el código que se deserializaría. Si se detecta una versión anterior, realice una pequeña transformación para que coincida con el nuevo formato y proceda según sea necesario. Una vez que se hayan transformado todos los datos, ese código puede quedar obsoleto en una versión futura.

+1

Gracias por su sugerencia. Esta será mi opción alternativa. Lo único que me preocupa un poco son los espacios de nombres complicados dentro de la salida de NetDataContractSerializer, que estoy seguro harán la escritura divertida de XSLT. Tal vez sea una buena razón con Linq para XML o simplemente la manipulación de tipo XML Dom. – jpierson

Cuestiones relacionadas