Compartir a través de


Serialización tolerante a versiones

En las versiones 1.0 y 1.1 de .NET Framework, era problemático crear tipos serializables que fueran reutilizables de una versión de una aplicación a la siguiente. Si se modificaba un tipo mediante la adición de más campos, se producían los siguientes problemas:

  • Las versiones más antiguas de una aplicación iniciaban excepciones cuando se les solicitaba deserializar nuevas versiones del tipo antiguo.

  • Las versiones más recientes de una aplicación iniciaban excepciones cuando deserializaban versiones más antiguas de un tipo al que le faltaban datos.

La Serialización tolerante a versiones (VST) es un conjunto de características incluidas en .NET Framework 2.0 que facilita, con el tiempo, la modificación de tipos serializables. En concreto, las características de VST están habilitadas para las clases a las que se ha aplicado el atributo SerializableAttribute. VTS permite agregar nuevos campos a esas clases sin que deje de existir la compatibilidad con otras versiones del tipo. Para obtener una aplicación de ejemplo que funcione, vea Version Tolerant Serialization Technology Sample.

Las características de VTS están habilitadas cuando se utiliza BinaryFormatter. Además, todas las características, excepto la tolerancia a datos extraños, también están habilitadas cuando se utiliza SoapFormatter. Para obtener más información sobre el uso de estas clases para la serialización, vea Serialización binaria.

Nota

La característica de tolerancia a datos extraños de BinaryFormatter puede que esté disponible para .NET Framework 1.1 como revisión más tarde.

Lista de características

Éstas son algunas de las características:

  • Tolerancia a datos extraños o no esperados. Esto permite a las versiones más recientes del tipo enviar datos a versiones anteriores.

  • Tolerancia a la falta de datos opcionales. Esto permite a las versiones más antiguas enviar datos a versiones más recientes.

  • Devoluciones de llamadas de serialización. Esto permite la configuración inteligente de valores predeterminados en aquellos casos en los que faltan datos.

Además, existe una característica para la declaración cuando se ha agregado un nuevo campo opcional. Ésta es la propiedad VersionAdded del atributo OptionalFieldAttribute.

Estas características se abordan con más detalle a continuación.

Tolerancia a datos extraños o no esperados

En el pasado, durante la deserialización, cualquier dato extraño o no esperado ocasionaba el inicio de excepciones. Con VTS, en la misma situación, se omite cualquier dato extraño o no esperado en lugar de que éste ocasione el inicio de excepciones. Esto permite a las aplicaciones que utilizan versiones más recientes de un tipo (es decir, una versión que incluye más campos) enviar información a aplicaciones que esperan versiones más antiguas del mismo tipo.

En el siguiente ejemplo, los datos adicionales incluidos en CountryField de la versión 2.0 de la clase Address se omiten cuando una aplicación más antigua deserializa la versión más reciente.

// Version 1 of the Address class.
[Serializable]
public class Address
{
    public string Street;
    public string City;
}
// Version 2.0 of the Address class.
[Serializable]
public class Address
{
    public string Street;
    public string City;
    // The older application ignores this data.
    public string CountryField;
}
' Version 1 of the Address class.
<Serializable> _
Public Class Address
    Public Street As String
    Public City As String
End Class

' Version 2.0 of the Address class.
<Serializable> _
Public Class Address
    Public Street As String
    Public City As String
    ' The older application ignores this data.
    Public CountryField As String
End Class

Tolerancia a la falta de datos

Es posible marcar los campos como opcionales aplicándoles el atributo OptionalFieldAttribute. Durante la deserialización, si faltan datos opcionales, el motor de serialización pasa por alto la ausencia y no inicia ninguna excepción. Por tanto, las aplicaciones que esperan versiones más antiguas de un tipo pueden enviar datos a aplicaciones que esperan versiones más recientes del mismo tipo.

El siguiente ejemplo muestra la versión 2.0 de la clase Address con el campo CountryField marcado como opcional. Si una aplicación más antigua envía la versión 1 a una aplicación más reciente que espera la versión 2.0, se pasa por alto la ausencia de los datos.

[Serializable]
public class Address
{
    public string Street;
    public string City;

    [OptionalField]
    public string CountryField;
}
<Serializable> _
Public Class Address
    Public Street As String
    Public City As String

    <OptionalField> _
    Public CountryField As String
End Class

Devoluciones de llamadas de serialización

Las devoluciones de llamadas de serialización son un mecanismo que proporciona enlaces al proceso de serialización y deserialización en cuatro puntos.

Atributo Cuándo se llama al método asociado Uso habitual

OnDeserializingAttribute

Antes de la deserialización.*

Inicializar los valores predeterminados para campos opcionales.

OnDeserializedAttribute

Después de la deserialización.

Corregir valores de campos opcionales basándose en el contenido de otros campos.

OnSerializingAttribute

Antes de la serialización.

Prepararse para la serialización. Por ejemplo, crear estructuras de datos opcionales.

OnSerializedAttribute

Después de la serialización.

Registrar eventos de serialización.

* Esta devolución de llamada se invoca antes que el constructor de deserialización, si existe.

Usar devoluciones de llamadas

Para utilizar devoluciones de llamadas, aplique el atributo correspondiente a un método que acepte un parámetro StreamingContext. Sólo es posible marcar un método por clase con cada uno de estos atributos. Por ejemplo:

[OnDeserializing]
private void SetCountryRegionDefault(StreamingContext sc)
{
    CountryField = "Japan";
}
[OnDeserializing]
private void SetCountryRegionDefault(StreamingContext sc)
{
    CountryField = "Japan";
}

El uso previsto de estos métodos es el control de versiones. Durante la deserialización, es posible que un campo opcional no se inicialice correctamente si faltan los datos del campo. Esto se puede corregir creando el método que asigne el valor correcto y, a continuación, aplicando el atributo OnDeserializingAttribute o OnDeserializedAttribute al método.

El siguiente ejemplo muestra el método en el contexto de un tipo. Si una versión anterior de una aplicación envía una instancia de la clase Address a una versión posterior de la aplicación, faltarán los datos del campo CountryField. Sin embargo, después de la deserialización, el campo se establecerá con el valor predeterminado "Japan".

[Serializable]
public class Address
{
    public string Street;
    public string City;
    [OptionalField]
    public string CountryField;

    [OnDeserializing]
    private void SetCountryRegionDefault (StreamingContext sc)
    {
        CountryField = "Japan";
    }
}
<Serializable> _
Public Class Address
    Public Street As String
    Public City As String
    <OptionalField> _
    Public CountryField As String

    <OnDeserializing> _
    Private Sub SetCountryRegionDefault(StreamingContext sc)
        CountryField = "Japan";
    End Sub
End Class

Propiedad VersionAdded

OptionalFieldAttribute posee la propiedad VersionAdded. En la versión 2.0 de .NET Framework no se utiliza esta propiedad. Sin embargo, es importante establecer esta propiedad correctamente para garantizar que el tipo sea compatible con futuros motores de serialización.

Esta propiedad indica qué versión de un tipo se ha agregado a un determinado campo. Debe incrementarse exactamente en uno (comenzando en 2) cada vez que se modifique el tipo, tal y como se muestra en el siguiente ejemplo:

// Version 1.0
[Serializable]
public class Person
{
    public string FullName;
}

// Version 2.0
[Serializable]
public class Person
{
    public string FullName;

    [OptionalField(VersionAdded = 2)]
    public string NickName;
    [OptionalField(VersionAdded = 2)]
    public DateTime BirthDate;
}

// Version 3.0
[Serializable]
public class Person
{
    public string FullName;

    [OptionalField(VersionAdded=2)]
    public string NickName;
    [OptionalField(VersionAdded=2)]
    public DateTime BirthDate;

    [OptionalField(VersionAdded=3)]
    public int Weight;
}
' Version 1.0
<Serializable> _
Public Class Person
    Public FullName
End Class

' Version 2.0
<Serializable> _
Public Class Person
    Public FullName As String

    <OptionalField(VersionAdded := 2)> _
    Public NickName As String
    <OptionalField(VersionAdded := 2)> _
    Public BirthDate As DateTime
End Class

' Version 3.0
<Serializable> _
Public Class Person
    Public FullName As String

    <OptionalField(VersionAdded := 2)> _
    Public NickName As String
    <OptionalField(VersionAdded := 2)> _
    Public BirthDate As DateTime

    <OptionalField(VersionAdded := 3)> _
    Public Weight As Integer
End Class

Procedimientos recomendados

Para garantizar un comportamiento correcto entre versiones, siga estas reglas cuando modifique un tipo de una versión a otra:

  • Nunca quite un campo serializado.

  • Nunca aplique el atributo NonSerializedAttribute a un campo si el atributo no se aplicó al campo en la versión anterior.

  • Nunca cambie el nombre o el tipo de un campo serializado.

  • Cuando agregue un nuevo campo serializado, aplique el atributo OptionalFieldAttribute.

  • Cuando quite un atributo NonSerializedAttribute de un campo (que no fue serializable en una versión anterior), aplique el atributo OptionalFieldAttribute.

  • En el caso de todos los campos opcionales, establezca valores predeterminados con significado utilizando las devoluciones de llamadas de serialización a menos que sean aceptables como valores predeterminados 0 o null .

Para garantizar que un tipo sea compatible con futuros motores de serialización, siga estas instrucciones:

  • Establezca siempre la propiedad VersionAdded del atributo OptionalFieldAttribute correctamente.

  • Evite las versiones bifurcadas.

Vea también

Referencia

SerializableAttribute
BinaryFormatter
SoapFormatter
VersionAdded
OptionalFieldAttribute
OnDeserializingAttribute
OnDeserializedAttribute
OnDeserializingAttribute
OnSerializedAttribute
StreamingContext
NonSerializedAttribute

Otros recursos

Serialización binaria