Serialización de datos en C# en Binario, Soap y Xml

Anuncio
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
Descargar