Serialización de datos en C# en Binario, Soap y Xml Quizás muchos desarrolladores hayan escuchado hablar del termino “serializar la información” y no saber de que se trata. Pues bien, resulta que la serialización de datos no es otra cosa que transformar los datos de tal manera que pueda ser transferida por un canal (Internet, archivo plano, memoria, etc) a otro sistema. En otras palabras, si queremos compartir información de nuestro sistemas con otras aplicaciones o viceversa, tendremos que utilizar serialización de datos. De manera nativa, el Framework de .NET nos ofrece la posibilidad de serializar la información en tres formatos: Binario, Soap, Xml. La serialización en formato binario consiste en convertir la información a bytes y se utiliza comúnmente para los escenarios donde la información es transferida por la red hacia un sistema destino, el cual recibe dicha información y realiza el proceso inverso de la serialización = Deserializacion para construir el objeto (información) que fue transferido. La serialización en formato Soap consiste en convertir los datos en un “documento estándar” en el cual se incluirá además de los datos a serializar, una serie de información adicional que será utilizada por el sistemas destino para construir el objeto original. Esta serialización es la que se utiliza en escenarios con Web Services. Finalmente la serialización en formato Xml, consiste en transformar la información en un documento Xml que será interpretado por el sistema destino. Los formatos de serialización Binario y Soap están contenidos en el namespace System.Runtime.Serialization, mientras que el Xml esta en el namespace System.Xml.Serialization. Para ejemplificar cada escenario, supongamos que en nuestra aplicación se utiliza una entidad llamada Empleado definida como se muestra a continuación: public class Empleado { private int identificacion; private string nombre; private string apellido; private int edad; private string telefono; private DateTime fechaIngreso; private int diasLaborables; public int Identificacion { get { return identificacion; } set { identificacion = value; } } public string Nombre { get { return nombre; } set { nombre = value; } } public string Apellido { get { return apellido; } set { apellido = value; } } public int Edad { get { return edad; } set { edad = value; } } public DateTime FechaIngreso { get { return fechaIngreso; } set { fechaIngreso = value; diasLaborables= (DateTime.Now.Subtract(fechaIngreso).Days); } } public string Telefono { get { return telefono; } set { telefono = value; } } public int diasLaborables { get { return diasLaborables; } set { diasLaborables = value; } } } Serialización en formato Binario Como se mencionó anteriormente, éste método se utiliza generalmente para intercambiar información con otros sistemas a través de la red. Sin embargo para agilidad en la ilustración de su funcionamiento, los datos serializados en este post, se enviarán a un archivo de texto para visualizar su contenido. Esta serialización tiene la desventaja que solo funcionará entre aplicaciones .NET, es decir, que tanto la aplicación que serializa como la aplicación que deserializa deben ser aplicaciones desarrolladas bajo la plataforma .NET. El primer paso para serializar un objeto en .NET, es incluir el atributo Serializable en la definición de nuestra clase así: [Serializable] public class Empleado {…] Esto le indicará al runtime de .NET que este objeto estará habilitado para ser serializado cuando se requiera, de lo contrario, se generará una excepción del tipo System.Runtime.Serialization.SerializationException. Después, implementar la serialización binaria es tan sencillo como invocar el método Serialize de la clase BinaryFormatter que encontramos en el namespace System.Runtime.Serialization. Este método recibe como parámetros un Stream y el objeto que deseamos serializar, por ejemplo: Si abrimos el archivo datoSerializado.txt, obtenemos: Como se ve en la figura, la información obtenida después del proceso de serialización contiene una serie de caracteres especiales. Esto es porque en realidad estamos serializando en formato binario y el bloc de notas no esta en capacidad de interpretar este tipo de información. Sin embargo, en la vida real, se supone que ese stream es enviado a través de la red y recibido por una aplicación destino, la cual utilizará el método Deserialize del objeto BinaryReader para obtener el objeto Empleado que fue enviado inicialmente, así: Lo cual da como resultado lo siguiente: Profundizando un poco más en como el runtime de .NET serializa los datos, debemos saber que en tiempo de ejecución el runtime convierte en bytes cada uno de los miembros del objeto sin importar el tipo o nivel de acceso (public, private, protected, etc). Esto en algunas ocasiones puede no ser lo que deseemos hacer, sino mas bien omitir algunos campos que consideramos no son necesarios al momento de serializar. En nuestro ejemplo especifico, para que queremos serializar el campo díasLaborados si puede ser calculado en tiempo de ejecución?. Esto implica un costo que podemos evitar ya que a mayor cantidad de miembros a serializar, mayor cantidad de información tendrá que ser transferida por red o almacenada en disco. Para modificar el comportamiento de la serialización binaria, podemos utilizar el atributo NonSerialized en cada uno de los miembros que deseamos omitir, así: [NonSerialized] private int diasLaborables; Con esto logramos que el resultado de la serialización sea el siguiente: Donde se puede ver que ya no existe ninguna información referente al miembro diasLaborables. Sin embargo, al momento de ejecutar la aplicación destino y deserializar el objeto, obtenemos lo siguiente: Donde podemos observar que no se está calculando el valor del campo diasLaborados. Esto se debe a que el proceso de deserialización lo que hace es “recrear” el estado del objeto serializado y como el campo diasLaborados no está incluido en la información serializada, es omitido en el proceso de deserialización. Por fortuna, existe la interfaz IDeserializationCallback, la cual incluye el método OnDeserialization que es llamado una vez termina el proceso de deserialización del objeto. Implementar esta interfaz solucionará nuestro problema ya que en dicho método podemos calcular el valor del miembro diasLaborados y así garantizamos que siempre que se realice una deserialización del objeto, se tendrá el estado completo del mismo. Para implementar la interfaz, se hace lo siguiente sobre el objeto Empleado: [Serializable] public class Empleado:IDeserializationCallback { //Definicon de miembros #region IDeserializationCallback Members public void OnDeserialization(object sender) { DiasLaborados = (DateTime.Now.Subtract(fechaIngreso).Days); } #endregion } Así, ejecutando nuevamente el código que se encarga de realizar la deserialización tenemos: Serialización en formato SOAP. La serialización en formato SOAP, consiste en utilizar un formateador especial que genera un documento SOAP con la información del objeto que deseamos serializar. Este formato de serialización es el utilizado en los servicios Web por su gran interoperabilidad entre sistemas ya que me permite tener los sistemas desarrollados bajo diferentes plataformas e incluso ejecutándose en diferentes plataformas (Linux, Windows, etc). Sin embargo, una de las desventajas que tiene este formato de serialización es que genera una gran cantidad de información al serializar nuestro objeto (debido a que se debe cumplir con el estándar de los documentos SOAP). Por este motivo, es necesario analizar que tipo de serialización utilizar en nuestra aplicación. Así entonces tenemos que la serialización binaria es la más eficiente pero sólo nos sirve para escenarios donde las aplicaciones están desarrolladas en .NET. Si por el contrario, necesitamos compartir nuestros datos con aplicaciones desarrolladas en otros lenguajes y además cumplir con el estándar SOAP, utilizaremos el formato SOAP y finalmente si lo que necesitamos es compartir información con aplicaciones desarrolladas en otros lenguajes pero no es necesario cumplir con ningún estándar, podemos utilizar el formato XML. Para serializar en formato SOAP, sólo se debe utilizar el formateador SoapFormatter que se encuentra en el namespace System.Runtime.Serialization.Formatters.Soap, así: Después de serializar obtenemos el siguiente resultado: Como se ve en la figura, el comportamiento de la serialización en formato SOAP también se ve afectada por el uso del atributo NonSerialized Serialización en formato XML. La serialización en formato XML consiste en transformar el objeto en un documento XML, donde por defecto, el nodo raíz será el tipo de dato que estemos serializando y sus miembros serán elementos de ese nodo raíz. La implementación de la serialización XML difiere un poco de las presentadas anteriormente ya que no se utiliza un formateador sino que se hace utilizando la clase XmlSerializer que se encuentra en el namespace System.Xml.Serialization. Esta clase recibe como parámetro el tipo de dato que deseamos serializar. Por ejemplo: De esta manera le estamos indicando al objeto XmlSerializer que deseamos serializar un objeto de tipo Empleado, el cual al momento de invocar el método Serialize, genera un documento XML como el siguiente: Como se puede ver en la figura anterior, el miembro diasLaborados fue serializado. Esto es porque la serialización XML no se ve afectada por el atributo NonSerialized. Sin embargo, la interface IDeserializationCallback si aplica para el proceso de la deserialización del objeto. Para modificar el comportamiento de la serialización XML, se debe usar el atributo XmlIgnore sobre el miembro que deseamos omitir, así: [XmlIgnore] public int DiasLaborados { get { return diasLaborados; } set { diasLaborados = value; } } Es de notar que el atributo se ha utilizado sobre la propiedad DiasLaborados y no sobre el miembro diasLaborados. Esto es porque la serialización XML serializa únicamente los miembros públicos del objeto, por lo cual si usamos el atributo XmlIgnore sobre el miembro privado diasLaborados, el XmlSerializer igual seguiría generando el elemento DiasLaborados en el documento XML. En el namespace System.Xml.Serialization, existen varios atributos que permiten modificar el comportamiento de la serialización XML y así obtener diferentes estructuras del documento XML generado. Uno de los atributos más usado es el XmlAttribute el cual se usa sobre cualquier miembro público y sirve para indicarle al runtime que dicho miembro lo genere como un atributo del nodo raíz y no como un elemento. Por ejemplo: [XmlAttribute] public int Identificacion { get { return identificacion; } set { identificacion = value; } } Luego de serializar nuevamente el objeto con el código ilustrado anteriormente, obtenemos: Donde se ve claramente que el miembro Identificación es un atributo del nodo Empleado y no un elemento de dicho nodo como se había generado en el ejemplo previo. www.asociacionaepi.es