Serialización
Serialización en C#
Hoy en día, el lenguaje C# es uno de los más usados para desarrollar aplicaciones de servidor. Uno de los problemas más frecuentes durante la comunicación en programación web es conseguir transferir objetos hacia y desde una aplicación. Para eso, el objeto se debe transformar en un formato universal. Esta transformación se llama serialización. Este proceso permite recuperar el objeto representado bajo un formato intercambiable; con frecuencia este formato tiene una forma textual.
Para que los datos puedan transitar, hay que usar un flujo de datos (llamado stream en inglés, de ahí el nombre de la clase base en C#: Stream). Estos flujos pueden tomar varias formas, como por ejemplo un flujo de datos en memoria (representado por la clase MemoryStream en C#) o incluso un flujo de datos hacia un archivo en el disco (representado por la clase FileStream en C#).
Hay varias maneras de serializar un objeto, y entre ellas encontramos las siguientes:
-
La serialización XML, que transforma el objeto en cadena de caracteres al formato XML.
-
La serialización JSON, que transforma el objeto en cadena de caracteres al formato JSON.
En este capítulo vamos a abordar esos dos modos de serialización para transformar un objeto a un formato dado, pero también para poder recuperar un objeto desde una fuente de datos en el formato retenido.
Antes de .NET 8, había...
Serialización XML
La serialización en XML (rXentensible Markup Language) se basa en un formato reconocido como un estándar en el sector. Usado durante mucho tiempo, el XML es un lenguaje de señalización bastante prolífico, pero de hecho también bastante potente y ampliable. En la actualidad todavía se usa, especialmente en el enfoque de archivos de configuración o incluso de mensajes de intercambios para los servicios que usan el protocolo SOAP.
.NET ofrece dos maneras de gestionar la serialización, que vamos a tratar en esta sección. Vamos a empezar por el enfoque de bajo nivel, más eficiente, pero también con más limitaciones.
1. XmlSerializer
La clase XmlSerializer, que se encuentra en el espacio de nombres System.Xml.Serialization, permite realizar la deserialización de objetos en XML. El funcionamiento de la clase necesita proporcionar un objeto y un stream. De igual forma, es necesario especificar el tipo que se ha de gestionar durante la creación de la instancia de la clase XmlSerializer, usando la palabra clave typeof:
var p = new PersonaXml { Nombre = " Christophe", Apellido = "Mommer" };
var serializer = new XmlSerializer(typeof(PersonaXml));
using (var stream = new FileStream("person.xml", FileMode.Create))
{
serializer.Serialize(stream, p);
}
using (var stream = new FileStream("person.xml", FileMode.Open))
{
var p2 = (PersonaXml)serializer.Deserialize(stream);
System.Console.WriteLine("Hola " + p2.Nombre + " " + p2.Apellido);
}
La declaración using utilizada en el bloque de código anterior difiere de la declaración using utilizada al principio de un archivo para importar un espacio de nombres. Para obtener más detalles sobre las declaraciones using en el código como se indicó anteriormente, el lector puede consultar el capítulo Conceptos avanzados, sección IDisposable e IAsyncDisposable para una mejor comprensión.
La serialización con este enfoque se basa en el uso de atributos que permiten decir lo que queremos serializar o no, y cómo debe hacerse la serialización.
Como recordatorio...
Serialización JSON
En la actualidad, el formato de datos JSON es el más usado para intercambiar información debido a su simplicidad, pero también a su ligereza sintáctica respecto al XML. Por ello, el framework .NET permite tratar el formato JSON de manera nativa.
Desde la versión 3 del framework .NET, los ingenieros de Microsoft han añadido directamente en el framework una manera nueva de tratar los flujos JSON, más eficiente que el planteamiento anterior, y todo esto para dejar de depender del paquete comunitario más ampliamente usado hasta entonces: NewtonSoft.Json.
A semejanza de la API proporcionada para el tratamiento XML, hay tres enfoques para tratar el JSON:
-
De manera procesal, leyendo el flujo de extremo a extremo con ayuda de un cursor gracias a las clases Utf8JsonReader y Utf8JsonWriter.
-
Con un enfoque orientado al documento, a semejanza de XDocument, gracias a la clase JsonDocument.
-
De manera extremadamente simplificada usando la clase estática JsonSerializer.
1. Utf8JsonReader y Utf8JsonWriter
El formato JSON es relativamente sencillo porque el tipo de datos encontrado durante la lectura de un flujo JSON forzosamente está contenido en la lista siguiente:
-
Inicio o final del objeto.
-
Inicio o final de la tabla.
-
Nombre de propiedad.
-
Valor (cadena de caracteres, numérica o booleana).
Por ello, el enfoque sostenido en la clase Utf8JsonReader se centra en la lectura de elementos uno por uno, y es función del desarrollador definir lo que quiere realizar.
Consideramos el siguiente flujo JSON:
var json = """
{
"Nombre" : "Christophe",
"Apellido" : "Mommer",
"Edad" : 35,
"Dirección" : {
"Calle": "Plaza del Reloj",
"Ciudad" : "Talavera de la Reina"
}
}
""";
Podemos usar la clase Utf8JsonReader para leer el flujo de manera procesal. Para poder usar esta clase, hay que construirla con una tabla de bytes que contiene el flujo JSON que se va a tratar. La clase Encoding, en el espacio de nombres System.Text, ofrece los distintos tipos de codificación disponibles para poder convertir cadenas a y desde tablas de bytes. Así, podemos...
Ejercicio
El propósito de este ejercicio es poner en práctica las distintas formas de serialización vistas en este libro, tanto de lectura como de escritura.
1. Enunciado
La base de trabajo es el proyecto desarrollado en el capítulo Programación orientada a objetos y en el capítulo Algoritmia: el juego para adivinar el número misterioso. Aquí, se trata de crear una tabla de puntuaciones que se mostrará debajo del menú del juego, proponiéndole al jugador introducir su nombre si se clasifica como uno de los cinco mejores jugadores.
Para hacerlo, es necesario calcular una cantidad de puntos correspondiente a la siguiente fórmula: número de posibilidades + (número de intentos que quedan elevado a la dificultad).
Por ejemplo, si un jugador ha intentado adivinar un número entre 1 y 100, hay 100 posibilidades. Si ha elegido jugar en modo fácil, el nivel de dificultad es 1. Entonces hay que poner la cantidad de intentos que quedan como potencia de este número. Si el jugador ha ganado en cuatro intentos, le quedan 6, lo que da 100 + 6 elevado a 1 = 106 puntos.
El operador C# que se usará para hacer una potencia es ^. Así, para almacenar en una variable 2 elevado a 3, se escribe var i = 2 ^ 3;.
Entonces hay que leer la tabla de las puntuaciones y ver si el jugador puede entrar en el grupo de los cinco mejores. En caso afirmativo, pedimos que introduzca su nombre para registrarlo en la posición correcta.
No hay limitaciones específicas sobre el tipo de serialización...