Serialización de objetos en XML con C#.NET

De manera análoga a como en Java es posible realizar la serialización de clases a XML, encontré que también es posible hacerlo en C#.NET.  Para esto es necesario que la clase a ser serializada exponga los atributos que desea publicar a través de propiedades.

Para la demostración creé una clase XMLSerializable de la cual heredarán las otras que requieran de este tipo de serialización.  Esta clase abstracta provee los métodos load y save para realizar automáticamente la serialización de sus hijos.

La serialización de los objetos se realiza con el siguiente código.

        public void save(String filename)
        {
            // Creates the writing stream

            StreamWriter w = new StreamWriter(filename);

            // Creates the XML serializer with its type

            XmlSerializer s = new XmlSerializer(this.GetType());

            // Serializes the object (this self -> son)

            s.Serialize(w, this);

            // Closes the output stream

            w.Close();
        }

La carga de objetos serializados requiere de un par de verificiones adicionales.

        public bool load(String filename)
        {
            bool success = false;

            // Checks if the source file exists, otherwise there is nothing to do

            if (File.Exists(filename))
            {
                // Creates the reading stream

                StreamReader sr = new StreamReader(filename);

                // Creates the text reader from the stream

                XmlTextReader xr = new XmlTextReader(sr);

                // Creates the XML (de)serializer with its type

                XmlSerializer xs = new XmlSerializer(this.GetType());

                // Temporary storage object

                object c;

                // Checks if the received data can be deserialized

                if (xs.CanDeserialize(xr))
                {
                    // Deserialize the incoming data

                    c = xs.Deserialize(xr);

                    // Gets the type (definition) of the object

                    Type t = this.GetType();

                    // Retrieve the exposed attributes by properties

                    PropertyInfo[] properties = t.GetProperties();

                    // Walks thru all properties and loads its values on
                    // the local attributes

                    foreach (PropertyInfo p in properties)
                    {
                        p.SetValue(this, p.GetValue(c, null), null);
                    }

                    success = true;
                }

                // Closes the reader and the stream

                xr.Close();
                sr.Close();
            }

            return success;
        }

Con estas facilidades se crea una clase Empleado con atributos básicos.

    public class Empleado : XMLSerializable
    {
        ////////////////////////////////////////////////////////////////

        private int      codigo;
        private String   nombre;
        private float    salario;
        private DateTime fechaNacimiento;

        ////////////////////////////////////////////////////////////////

        // Resto del codigo
    }

Es necesario para la serialización que el objeto tenga propiedades de los atributos a exponer.

        public int Codigo
        {
            get { return codigo; }
            set { codigo = value; }
        }

        public String Nombre
        {
            get { return nombre; }
            set { nombre = value; }
        }

        public float Salario
        {
            get { return salario; }
            set { salario = value; }
        }

        public DateTime FechaNacimiento
        {
            get { return fechaNacimiento; }
            set { fechaNacimiento = value; }
        }

También es necesario que la clase cuente con un constructor sin parámetros además de los que requiera normalmente.

        public Empleado()
        {
            this.codigo          = -1;
            this.nombre          = "Sin nombre";
            this.salario         = -1;
            this.fechaNacimiento = new DateTime(1990, 01, 01);
        }

        ////////////////////////////////////////////////////////////////

        public Empleado(int codigo, String nombre, float salario, DateTime fechaNacimiento)
        {
            this.codigo          = codigo;
            this.nombre          = nombre;
            this.salario         = salario;
            this.fechaNacimiento = fechaNacimiento;
        }

Por facilidad también se sobreescribió el método ToString() para generar su representación en cadena.

        override public String ToString()
        {
            return String.Format("[Empleado: codigo = {0}, nombre = "{1}", salario = ${2}, fechaNacimiento = {3}]",
                                    this.codigo, this.nombre, this.salario, this.fechaNacimiento);
        }

Con todo esto, la aplicación de prueba crea en el primer paso a un objeto Empleado y le da valores iniciales a sus atributos, después de esto solicita su serialización.

            Empleado empleado = new Empleado(123, "Pepito Pimentón", 123456.789f, DateTime.Now);
            empleado.save("empleado.xml");

Posteriormente se crea un segundo Empleado que almacenará la información recuperada de la serialización.  Si la prueba tiene éxito, la información de los dos Empleados deberá coincidir.

            Empleado empleado2 = new Empleado();
            empleado2.load("empleado.xml");

La información presentada a través de salida estándar al ejecutar la aplicación de demostración deberá ser algo similar a lo siguiente.

Writing Object: [Empleado: codigo = 123, nombre = "Pepito Pimentón", salario = $123456,8, fechaNacimiento = 14/09/2008 11:12:10 p.m.]
Read Object:    [Empleado: codigo = 123, nombre = "Pepito Pimentón", salario = $123456,8, fechaNacimiento = 14/09/2008 11:12:10 p.m.]

El documento XML generado es bastante explícito y fácil de realizar su manipulación manual si se llegara a requerir.



  123
  Pepito Pimentón
  123456.789
  2008-09-14T22:39:52.888-05:00

Enlaces:

Serialización con XML

Esta semana encontré algo que me pareció interesante.  Es la posibilidad de serializar objetos en Java utilizando un codificador XML.  Lo mas interesante se que para realizar esta codificación básica no es necesario de ningún OXM (Object XML Mapper), por el contrario, todo lo necesario viene incluído ya en el J2SE.

Para el ejemplo he creado una clase Worker (POJO) que será serializada y sólo incluye atributos y sus respectivos métodos set/get.  Por presentación también sobreescribí al método toString para presentar el contenido del objeto.

public class Worker
{
    private String username;
    private String password;
    private String name;
    private int age;
    private Date birthDate;

    // ...
}

Creo una instancia de esta clase y asigno valores a sus atributos.

GregorianCalendar birthDate = new GregorianCalendar(2005, 02, 14);  

Worker workerman = new Worker();

workerman.setAge(31);
workerman.setName("Pepito Pimentón");
workerman.setUsername("pepitouser");
workerman.setPassword("pepitopass");
workerman.setBirthDate(birthDate.getTime());

A continuación se procede a codificar el objeto y a generar el archivo XML con su contenido.

// Creates the stream to the file that will storage the serialized object
FileOutputStream outputFile = new FileOutputStream("workerman.xml");

// Relates the XML encoder with the output file stream
XMLEncoder xe = new XMLEncoder(outputFile);

// Serializes the selected object using an XML encoding
xe.writeObject(workerman);

// Closes the XML encoder
xe.close();

Terminado este proceso, deberá existir el archivo workerman.xml con la representación XML del objeto workerman creado anteriormente.  Esta representación de la información podría ser compartida inclusive con otros sistemas/lenguajes/plataformas diferentes a la actual, cosa que podría tener algunos inconvenientes con la serialización convencional de java.io.  El contenido XML (texto plano) probablemente ocupe mas espacio y esto lo haga menos eficiente que una representación binaria.

Por supuesto también es posible realizar el proceso contrario: basados en el archivo workerman.xml con el contenido codificado, obtener la información y crear nuevamente la representación del objeto Worker.

// Creates the stream from the file that storages the already serialized object
inputFile = new FileInputStream("workerman.xml");

// Relates the XML decoder with the input file stream
XMLDecoder xd = new XMLDecoder(inputFile);

// Reads the object from the stream and deserializes it using an XML decoding
clone = (Worker)xd.readObject();

// Closes the XML decoder
xd.close();

El objeto clone deberá contener la misma información que su versión original: workerman.

El contenido del archivo workerman.xml es bastante explícito para su procesamiento en otras plataformas.



 
  
   31
  
  
   
    1110776400000
   
  
  
   Pepito Pimentón
  
  
   pepitopass
  
  
   pepitouser
  
 

Enlace:   XML Serialization Demo (fuentes).

Primer ejemplo de XML-RPC

Estimando que probablemente uno de los proyectos de la Fundación requiera la consulta de información de uno de los proyectos anteriores ubicado en uno de los servicios de hosting, decidí realizar algunos experimientos con XML-RPC para determinar la viabilidad de utilizarlo para esta tarea en caso de que sea realmente necesaria. Debido a que el hosting en el que se encuentra el proyecto tiene una versión bastante antigua de PHP4 se decidió utilizar la librería de http://phpxmlrpc.sourceforge.net/.

Este es mi primer contacto con XML-RPC. Había leido un poco del tema en la documentación del framework CodeIgniter que planeo utilizar dentro de poco en mis próximos proyectos, sin embargo como está basado en PHP5 y el hosting soporta únicamente PHP4 decidí probar con la librería mencionada anteriormente, la cual es bastante mas enrredada, pero por suerte bien documentada, aunque hacen falta ejemplos.

En este pequeño demo que implementé se envían por parte del cliente un nombre (“Pepe”) y la solicitud de ejecución de un procedimiento remoto (“HelloWorld.show”). El servidor relaciona este método remoto con un procedimiento local (“show”) que retorna la cadena resultante (“Hello Pepe”).

El Cliente.

Se realiza el include de la librería de XML-RPC.

include_once (“../lib/xmlrpc-2.2/lib/xmlrpc.inc”);

Se define la ubicación del servidor y se instancia un objeto de la clase cliente.

$host = “localhost”;
$location = “xmlrpc/d1”;
$server = “ServerHello.php”;

$client = new xmlrpc_client(“http://{$host}/{$location}/{$server}”);

Se prepara el mensaje que enviará el cliente. El mensaje define cual es el procedimiento remoto a ejecutarse y la información relacionada con este (parámetros). Para el ejemplo se invocará al procedimiento HelloWorld.show y se le enviará “Pepe” que es de tipo string.

$message = new xmlrpcmsg(“HelloWorld.show”, array(new xmlrpcval(“Pepe”, “string”)));

Si se desea consultar los mensajes de depuración se debe activar la siguiente expresión.

// $client -> setDebug(3);

Con todo listo, se envía la solicitud al servidor y se captura su respuesta.

$response = $client -> send($message);

Si la respuesta referencia a un error, este viene con su respectivo código de fallo, en caso contrario se puede consultar su contenido.

if ($response -> faultCode())
echo “<br>KO. Error: ” . $response -> faultString();
else
echo “<br>OK. got … ” . $response -> value() -> scalarVal();

El Servidor.

Se realiza el include de la librería de XML-RPC, se incluyen los dos archivos inc: cliente y servidor.

include_once (“../lib/xmlrpc-2.2/lib/xmlrpc.inc”);
include_once (“../lib/xmlrpc-2.2/lib/xmlrpcs.inc”);

Se instancia un objeto de clase servidor en la que se especifica la siguiente información de los procedimientos remotos expuestos como disponibles:

  • Relación entre nombre de procedimiento remoto y función PHP a ejecutarse en su llamado.
  • La firma de la función (signature): tipo de retorno y de parámetros requeridos. Una función puede tener varias firmas.
  • Documentación de la función (docstring).

$server = new xmlrpc_server(array(
“HelloWorld.show” => array(“function” => “show”,
“signature” => array(array($xmlrpcValue, $xmlrpcString)),
“docstring” => “Shows a greeting message with the specified name.”)
), false);

Si se desea consultar los mensajes de depuración se debe activar la siguiente expresión.

// $server -> SetDebug(3);

Cuando todo está listo se envía la respuesta al cliente.

$server -> service();

Como siguiente paso se debe definir la función PHP show que será ejecutada cuando el procedimiento remoto HelloWorld.show sea solicitado.

La función recibe la información enviada por el cliente ($xmlrpcmsg) y tiene acceso a la variable $xmlrpcerruser la cual indica el número del error a partir del cual se encuentran disponibles para los errores definidos por el usuario.

function show($xmlrpcmsg)
{
global $xmlrpcerruser;

Verifico que el número de parámetros enviados por el cliente sea de uno, en caso contrario envío un código de error.

if ($xmlrpcmsg -> getNumParams() != 1)
{
return new xmlrpcresp(0, $xmlrpcerruser+1, “I am expecting a parameter for the name!”);
}

Obtengo el valor del parámetro disponible.

$val0 = $xmlrpcmsg -> getParam(0);

if($val0 -> kindOf() != “scalar”)
{
return new xmlrpcresp(0, $xmlrpcerruser+2, “I am expecting a string as parameter for the name!”);
}

$name = $val0 -> scalarval();

Compongo la cadena resultante: “Hello ” + valor del parámetro y la empaqueto en un mensaje de respuesta (xmlrpcresp) el cual es de tipo string y es enviado hacia el cliente.

return new xmlrpcresp(new xmlrpcval(“Hello ” . $name, “string”));
}

El demo #1 en ejecución puede ser consultado en http://demo.jorgeivanmeza.com/PHP/XMLRPCDemo/0.1/ClientHello.php

La documentación en línea de XML-RPC for PHP puede ser consultada en http://phpxmlrpc.sourceforge.net/doc-2/.