Serialización personalizadaCustom serialization

La serialización personalizada es el proceso de controlar la serialización y deserialización de un tipo.Custom serialization is the process of controlling the serialization and deserialization of a type. Controlando la serialización, es posible asegurarse compatibilidad de la serialización, que es la capacidad para serializar y deserializar entre las versiones de un tipo sin interrumpir la función básica del tipo.By controlling serialization, it's possible to ensure serialization compatibility, which is the ability to serialize and deserialize between versions of a type without breaking the core functionality of the type. En la primera versión de un tipo, puede haber por ejemplo, solo dos campos.For example, in the first version of a type, there may be only two fields. En la versión siguiente de un tipo, se agregan varios campos más.In the next version of a type, several more fields are added. Todavía la segunda versión de una aplicación debe poder serializar y deserializar ambos tipos.Yet the second version of an application must be able to serialize and deserialize both types. En las secciones siguientes se describe cómo controlar la serialización.The following sections describe how to control serialization.

Advertencia

La serialización binaria puede ser peligrosa.Binary serialization can be dangerous. No debe deserializar nunca datos de una fuente no fiable ni incluir datos serializados de ida y vuelta en un sistema que no controla usted.Never deserialize data from an untrusted source and never round-trip serialized data to systems not under your control.

Importante

En versiones previas a .NET Framework 4.0, la serialización de datos de usuario personalizados de un ensamblado de confianza parcial se realizaba mediante el método GetObjectData.In versions previous to .NET Framework 4.0, serialization of custom user data in a partially trusted assembly was accomplished using the GetObjectData. A partir de la versión 4.0, ese método se marca con el atributo de la clase SecurityCriticalAttribute que impide la ejecución en ensamblados de confianza parcial.Starting with version 4.0, that method is marked with the SecurityCriticalAttribute attribute which prevents execution in partially trusted assemblies. Para solucionarlo, implemente la interfaz ISafeSerializationData.To work around this condition, implement the ISafeSerializationData interface.

Ejecutar los métodos personalizados durante y después de la serializaciónRunning custom methods during and after serialization

El procedimiento recomendado y la manera más fácil (se introduce en la versión 2.0 de .NET Framework) es aplicar los atributos siguientes a los métodos que se utilizan para corregir los datos durante y después de la serialización:The best practice and easiest way (introduced in version 2.0 of the .NET Framework) is to apply the following attributes to methods that are used to correct data during and after serialization:

Estos atributos permiten al tipo participar en cualquiera de las cuatro fases de los procesos de deserialización y serialización.These attributes allow the type to participate in any one of, or all four of the phases, of the serialization and deserialization processes. Los atributos especifican los métodos del tipo que se debe invocar durante cada fase.The attributes specify the methods of the type that should be invoked during each phase. Los métodos no tienen acceso a la secuencia de la serialización pero en su lugar le permiten modificar el objeto antes de y después de la serialización o antes de y después de la deserialización.The methods do not access the serialization stream but instead allow you to alter the object before and after serialization, or before and after deserialization. Los atributos se pueden aplicar en todos los niveles de la jerarquía de herencia de tipo y se llama a cada método en la jerarquía desde la base a los más derivados.The attributes can be applied at all levels of the type inheritance hierarchy, and each method is called in the hierarchy from the base to the most derived. Este mecanismo evita la complejidad y cualquier problema resultante de implementar la interfaz ISerializable proporcionando la responsabilidad por la serialización y deserialización a la implementación más derivada.This mechanism avoids the complexity and any resulting issues of implementing the ISerializable interface by giving the responsibility for serialization and deserialization to the most derived implementation. Además, este mecanismo permite a los formateadores omitir la población de campos y recuperación de la secuencia de la serialización.Additionally, this mechanism allows the formatters to ignore the population of fields and retrieval from the serialization stream. Para obtener los detalles y ejemplos de controlar serialización y deserialización, haga clic en cualquiera de los vínculos anteriores.For details and examples of controlling serialization and deserialization, click any of the previous links.

Además, al agregar un nuevo campo a un tipo serializable existente, aplique el atributo OptionalFieldAttribute al campo.In addition, when adding a new field to an existing serializable type, apply the OptionalFieldAttribute attribute to the field. BinaryFormatter y SoapFormatter omite la ausencia del campo cuando se procesa una secuencia que falta en el nuevo campo.The BinaryFormatter and the SoapFormatter ignores the absence of the field when a stream that is missing the new field is processed.

Implementar la interfaz ISerializableImplementing the ISerializable interface

La otra manera de controlar la serialización se logra implementando la interfaz ISerializable en un objeto.The other way to control serialization is achieved by implementing the ISerializable interface on an object. Tenga en cuenta, sin embargo, que el método en la sección anterior reemplaza este método para controlar la serialización.Note, however, that the method in the previous section supersedes this method to control serialization.

Además, no debe usar la serialización predeterminada en una clase que se marca con el atributo Serializable y tiene declaración o seguridad imperativa en el nivel de clase o en sus constructores.In addition, you should not use default serialization on a class that is marked with the Serializable attribute and has declarative or imperative security at the class level or on its constructors. En su lugar, estas clases siempre deben implementar la interfaz ISerializable.Instead, these classes should always implement the ISerializable interface.

Implementar ISerializable, implica implementar el método GetObjectData y un constructor especial que se usa cuando se deserializa el objeto.Implementing ISerializable involves implementing the GetObjectData method and a special constructor that is used when the object is deserialized. El código de ejemplo siguiente muestra cómo implementar ISerializable en la clase MyObject de una sección anterior.The following sample code shows how to implement ISerializable on the MyObject class from a previous section.

[Serializable]
public class MyObject : ISerializable
{
    public int n1;
    public int n2;
    public String str;

    public MyObject()
    {
    }

    protected MyObject(SerializationInfo info, StreamingContext context)
    {
      n1 = info.GetInt32("i");
      n2 = info.GetInt32("j");
      str = info.GetString("k");
    }

    [SecurityPermissionAttribute(SecurityAction.Demand, SerializationFormatter = true)]
    public virtual void GetObjectData(SerializationInfo info, StreamingContext context)
    {
        info.AddValue("i", n1);
        info.AddValue("j", n2);
        info.AddValue("k", str);
    }
}
<Serializable()>  _
Public Class MyObject
    Implements ISerializable
    Public n1 As Integer
    Public n2 As Integer
    Public str As String

    Public Sub New()
    End Sub

    Protected Sub New(ByVal info As SerializationInfo, ByVal context As StreamingContext)
        n1 = info.GetInt32("i")
        n2 = info.GetInt32("j")
        str = info.GetString("k")
    End Sub 'New

    <SecurityPermissionAttribute(SecurityAction.Demand, SerializationFormatter := True)> _
    Public Overridable Sub GetObjectData(ByVal info As SerializationInfo, ByVal context As StreamingContext)
        info.AddValue("i", n1)
        info.AddValue("j", n2)
        info.AddValue("k", str)
    End Sub
End Class

Cuando se llama a GetObjectData durante la serialización, es responsable de rellenar el elemento SerializationInfo proporcionado por la llamada al método.When GetObjectData is called during serialization, you are responsible for populating the SerializationInfo provided with the method call. Agregue las variables que se van a serializar como nombre y pares de valor.Add the variables to be serialized as name and value pairs. Cualquier texto se puede utilizar como nombre.Any text can be used as the name. Tiene la libertad para decidir qué variables miembro se agregan a SerializationInfo, con tal de que los datos suficientes se serialicen para restaurar el objeto durante la deserialización.You have the freedom to decide which member variables are added to the SerializationInfo, provided that sufficient data is serialized to restore the object during deserialization. Las clases derivadas deben llamar al método GetObjectData en el objeto base si el último implementa ISerializable.Derived classes should call the GetObjectData method on the base object if the latter implements ISerializable.

Observe que la serialización puede permitir a otro código ver o modificar datos de instancia de objeto que de lo contrario son inaccesibles.Note that serialization can allow other code to see or modify object instance data that is otherwise inaccessible. Por consiguiente, el código que realiza la serialización requiere SecurityPermission con la marca SerializationFormatter especificada.Therefore, code that performs serialization requires the SecurityPermission with the SerializationFormatter flag specified. De acuerdo con la directiva predeterminada, no se concede este permiso al código descargado de Internet o de la intranet; únicamente el código del equipo local tiene garantizado este permiso.Under default policy, this permission is not given to Internet-downloaded or intranet code; only code on the local computer is granted this permission. El método GetObjectData se debe proteger explícitamente o exigiendo el elemento SecurityPermission con la marca SerializationFormatter especificada o exigiendo otros permisos que específicamente ayudan a proteger los datos privados.The GetObjectData method must be explicitly protected either by demanding the SecurityPermission with the SerializationFormatter flag specified or by demanding other permissions that specifically help protect private data.

Si un campo privado almacena información confidencial, debe exigir los permisos adecuados en GetObjectData para proteger los datos.If a private field stores sensitive information, you should demand the appropriate permissions on GetObjectData to protect the data. Recuerde que ese código al que se le ha concedido SecurityPermission con la marca SerializationFormatter especificada puede ver y modificar los datos almacenados en campos privados.Remember that code that has been granted SecurityPermission with the SerializationFormatter flag specified can view and modify the data stored in private fields. Un autor de llamada malintencionado al que se le haya concedido este SecurityPermission puede ver datos como ubicaciones del directorio ocultas o permisos concedidos y usar los datos para aprovecharse de una vulnerabilidad de seguridad en el equipo.A malicious caller granted this SecurityPermission can view data such as hidden directory locations or granted permissions and use the data to exploit a security vulnerability on the computer. Para obtener una lista completa de marcas de permiso de seguridad que puede especificar, vea SecurityPermissionFlag Enumeration.For a complete list of the security permission flags you can specify, see the SecurityPermissionFlag Enumeration.

Es importante enfatizar que cuando ISerializable se agrega a una clase, se debe implementar GetObjectData y el constructor especial.It's important to stress that when ISerializable is added to a class you must implement both GetObjectData and the special constructor. El compilador proporciona una advertencia si falta GetObjectData.The compiler warns you if GetObjectData is missing. Sin embargo, porque es imposible de exigir la implementación de un constructor, no se proporciona ninguna advertencia si el constructor está ausente, y se produce una excepción cuando se intenta deserializar una clase sin el constructor.However, because it is impossible to enforce the implementation of a constructor, no warning is provided if the constructor is absent, and an exception is thrown when an attempt is made to deserialize a class without the constructor.

El diseño actual se favoreció sobre un método SetObjectData para ir alrededor de la seguridad potencial y los problemas controlando las versiones.The current design was favored above a SetObjectData method to get around potential security and versioning problems. Por ejemplo, un método SetObjectData debe ser público si se define como parte de una interfaz; así los usuarios deben escribir el código para defenderse de llamar varias veces al método SetObjectData.For example, a SetObjectData method must be public if it is defined as part of an interface; thus users must write code to defend against having the SetObjectData method called multiple times. De lo contrario, una aplicación malintencionada que llama al método SetObjectData en un objeto en el proceso de ejecutar una operación puede producir los posibles problemas.Otherwise, a malicious application that calls the SetObjectData method on an object in the process of executing an operation can cause potential problems.

Durante la deserialización, SerializationInfo se pasa a la clase utilizando el constructor proporcionado para este propósito.During deserialization, SerializationInfo is passed to the class using the constructor provided for this purpose. Se omite cualquier restricción de visibilidad colocada en el constructor cuando se deserializa el objeto; así que puede marcar la clase como pública, protegida, interna o privada.Any visibility constraints placed on the constructor are ignored when the object is deserialized; so you can mark the class as public, protected, internal, or private. Sin embargo, es un procedimiento recomendado para proteger al constructor a menos que se selle la clase, en cuyo caso el constructor se debe marcar privado.However, it is a best practice to make the constructor protected unless the class is sealed, in which case the constructor should be marked private. El constructor también debe realizar la validación de entrada en profundidad.The constructor should also perform thorough input validation. Para evitar el mal uso por código dañino, el constructor debe exigir las mismas comprobaciones de seguridad y permisos exigidos para obtener una instancia de la clase utilizando cualquier otro constructor.To avoid misuse by malicious code, the constructor should enforce the same security checks and permissions required to obtain an instance of the class using any other constructor. Si no sigue esta recomendación, el código malintencionado puede preserializar un objeto, obtener el control con el SecurityPermission con el marcador SerializationFormatter especificado y deserializar el objeto en un equipo cliente que omite cualquier seguridad que se habría aplicado durante la construcción de la instancia estándar usando un constructor público.If you do not follow this recommendation, malicious code can preserialize an object, obtain control with the SecurityPermission with the SerializationFormatter flag specified and deserialize the object on a client computer bypassing any security that would have been applied during standard instance construction using a public constructor.

Para restaurar el estado del objeto, simplemente recupere los valores de las variables de SerializationInfo utilizando los nombres utilizó durante la serialización.To restore the state of the object, simply retrieve the values of the variables from SerializationInfo using the names used during serialization. Si la clase base implementa ISerializable, se debe llamar al constructor base para permitir al objeto base restaurar sus variables.If the base class implements ISerializable, the base constructor should be called to allow the base object to restore its variables.

Al derivar una nueva clase de uno que implementa ISerializable, la clase derivada debe implementar ambos, el constructor y el método GetObjectData si tiene variables que necesitan serializarse.When you derive a new class from one that implements ISerializable, the derived class must implement both the constructor as well as the GetObjectData method if it has variables that need to be serialized. El ejemplo de código siguiente muestra cómo esto se hace utilizando la clase MyObject mostrada previamente.The following code example shows how this is done using the MyObject class shown previously.

[Serializable]
public class ObjectTwo : MyObject
{
    public int num;

    public ObjectTwo()
      : base()
    {
    }

    protected ObjectTwo(SerializationInfo si, StreamingContext context)
      : base(si, context)
    {
        num = si.GetInt32("num");
    }

    [SecurityPermissionAttribute(SecurityAction.Demand, SerializationFormatter = true)]
    public override void GetObjectData(SerializationInfo si, StreamingContext context)
    {
        base.GetObjectData(si,context);
        si.AddValue("num", num);
    }
}
<Serializable()>  _
Public Class ObjectTwo
    Inherits MyObject
    Public num As Integer

    Public Sub New()

    End Sub

    Protected Sub New(ByVal si As SerializationInfo, _
    ByVal context As StreamingContext)
        MyBase.New(si, context)
        num = si.GetInt32("num")
    End Sub

    <SecurityPermissionAttribute(SecurityAction.Demand, SerializationFormatter := True)> _
    Public Overrides Sub GetObjectData(ByVal si As SerializationInfo, ByVal context As StreamingContext)
        MyBase.GetObjectData(si, context)
        si.AddValue("num", num)
    End Sub
End Class

No se olvide de llamar a la clase base en el constructor de deserialización.Don't forget to call the base class in the deserialization constructor. Si no se hace esto, nunca se llama al constructor en la clase base y el objeto no se construye totalmente después de la deserialización.If this isn't done, the constructor on the base class is never called, and the object is not fully constructed after deserialization.

Los objetos se reconstruyen al revés; y llamar a los métodos durante la deserialización puede tener efectos secundarios indeseables, porque los métodos llamados podrían hacer referencia a las referencias que no se han deserializado cuando se realiza la llamada.Objects are reconstructed from the inside out; and calling methods during deserialization can have undesirable side effects, because the methods called might refer to object references that have not been deserialized by the time the call is made. Si la clase deserializada implementa IDeserializationCallback, se llama al método OnDeserialization automáticamente cuando se ha deserializado el gráfico de objetos completo.If the class being deserialized implements the IDeserializationCallback, the OnDeserialization method is automatically called when the entire object graph has been deserialized. Se han restaurado todos los objetos secundarios hechos referencia totalmente en este punto.At this point, all the child objects referenced have been fully restored. Una tabla hash es un ejemplo típico de una clase que es difícil de deserializar sin utilizar el agente de escucha de evento.A hash table is a typical example of a class that is difficult to deserialize without using the event listener. Es fácil de recuperar los pares de valor y clave durante la deserialización, pero volver a agregar estos objetos a la tabla hash puede producir los problemas, porque no hay ninguna garantía de que se hayan deserializado las clases que derivaron de la tabla hash.It is easy to retrieve the key and value pairs during deserialization, but adding these objects back to the hash table can cause problems, because there is no guarantee that classes that derived from the hash table have been deserialized. Llamar a los métodos en una tabla hash en esta copia intermedia no es, por consiguiente, aconsejable.Calling methods on a hash table at this stage is therefore not advisable.

Vea tambiénSee also