Serialización y Deserialización de Objetos XML en C#

En el desarrollo de aplicaciones con C#, especialmente en entornos como WPF, es común enrfentar el desafío de persistir modelos de datos complejos en una base de datos. Dado que estos modelos suelen contener numerosas propiedades, almacenar cada campo de manera individual puede resultar poco práctico. Una solución eficiente es serializar el objeto completo a una cadena XML y guardarla en la base de datos. Al iniciar la aplicación nuevamente, esta cadena puede deserializarse para reconstruir el objeto original.

Implementación de un Conversor XML Genérico

La clase XmlSerializer proporcionada por .NET facilita este proceso. A continuación, se presenta una clase de utilidad refactorizada que utiliza StringWriter y StringReader para simplificar la conversión entre objetos y cadenas XML, mejorando la gestión de recursos mediante bloques using.


using System;
using System.IO;
using System.Xml;
using System.Xml.Serialization;

public static class XmlDataConverter
{
    public static string ConvertToXml<T>(T entity) where T : class
    {
        if (entity == null) return null;
        
        try
        {
            var serializer = new XmlSerializer(typeof(T));
            using var stringWriter = new StringWriter();
            using var xmlWriter = XmlWriter.Create(stringWriter, new XmlWriterSettings { Indent = true });
            
            serializer.Serialize(xmlWriter, entity);
            return stringWriter.ToString();
        }
        catch (Exception)
        {
            return null;
        }
    }

    public static T RestoreFromXml<T>(string xmlData) where T : class
    {
        if (string.IsNullOrWhiteSpace(xmlData)) return null;
        
        try
        {
            var serializer = new XmlSerializer(typeof(T));
            using var stringReader = new StringReader(xmlData);
            return (T)serializer.Deserialize(stringReader);
        }
        catch (Exception)
        {
            return null;
        }
    }
}

Manejo de Tipos No Serializables

Durante la serialización, es crucial tener en cuenta que todas las propiedades expuestas deben ser tipos serializables. En WPF, objetos visuales como BitmapImage o SolidColorBrush no pueden ser serializados directamente. Si se intenta procesar un objeto que los contenga, el sistema lanzará una excepción o devolverá un resultado nulo.

La estrategia correcta es aplicar el atributo [XmlIgnore] a estas propiedades para que el motor de serialización las omita. Sin embargo, para no perder la información, se debe crear una propiedad proxy que almacene los datos en un formato compatible, como un array de bytes.


using System.Windows.Media;
using System.Xml.Serialization;

public class ThemeSettings
{
    private SolidColorBrush _primaryBrush;

    [XmlIgnore]
    public SolidColorBrush PrimaryBrush
    {
        get => _primaryBrush;
        set
        {
            if (_primaryBrush != value)
            {
                _primaryBrush = value;
                // Lógica de notificación de propiedad (INotifyPropertyChanged)
            }
        }
    }

    [XmlArray("colorComponents")]
    [XmlArrayItem("component")]
    public byte[] PrimaryColorBytes
    {
        get
        {
            if (_primaryBrush == null) return new byte[4];
            var c = _primaryBrush.Color;
            return new byte[] { c.A, c.R, c.G, c.B };
        }
        set
        {
            if (value != null && value.Length == 4)
            {
                var restoredColor = Color.FromArgb(value[0], value[1], value[2], value[3]);
                PrimaryBrush = new SolidColorBrush(restoredColor);
            }
        }
    }
}

En el ejemplo anterior, los valores ARGB del color se extraen y se almacenan en un array de bytes. Los atributos [XmlArray] y [XmlArrayItem] permiten controlar la estructura de los nodos XML generados para esta colección. Al deserializar, el array de bytes se lee y se utiliza para reconstruir el objeto SolidColorBrush mediante el método Color.FromArgb.

Serialización de Colecciones y Atributos XML

Cuando se trabaja con arrays o listas de objetos, el motor de serialización XML requiere indicaciones explícitas sobre cómo estructurar los nodos. Los atributos XmlArrayAttribute y XmlArrayItemAttribute son fundamentales para definir los nombres de los elemantos contenedores y de los ítems individuales.

A continuación, se muestra un ejemplo que ilustra el uso de diversas etiquetas XML para controlar la salida de una colección de objetos.


using System;
using System.Xml.Serialization;

namespace XmlSerializationDemo
{
    class Program
    {
        static void Main(string[] args)
        {
            var fleet = new VehicleFleet
            {
                Automobiles = new Vehicle[]
                {
                    new Vehicle { Model = "Sedan", HorsePower = 150, Description = "Reliable family car." },
                    new Vehicle { Model = "SUV", HorsePower = 250, Description = "Off-road capable vehicle." }
                }
            };

            var serializer = new XmlSerializer(typeof(VehicleFleet));
            serializer.Serialize(Console.Out, fleet);
        }
    }

    [XmlRoot("fleet")]
    public class VehicleFleet
    {
        [XmlArray("vehicles")]
        [XmlArrayItem("vehicle")]
        public Vehicle[] Automobiles { get; set; }
    }

    public class Vehicle
    {
        [XmlAttribute("model")]
        public string Model { get; set; }

        [XmlIgnore]
        public int HorsePower { get; set; }

        [XmlElement("desc")]
        public string Description { get; set; }
    }
}

El código anterior produce la siguiente estructura XML, donde se puede observar cómo la propiedad Model se convierte en un atributo del nodo, HorsePower se omite por completo, y Description se serializa como un elemanto hijo con un nombre personalizado.


<?xml version="1.0" encoding="utf-8"?>
<fleet xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <vehicles>
    <vehicle model="Sedan">
      <desc>Reliable family car.</desc>
    </vehicle>
    <vehicle model="SUV">
      <desc>Off-road capable vehicle.</desc>
    </vehicle>
  </vehicles>
</fleet>

Etiquetas: C# XML Serialization WPF XmlSerializer .NET

Publicado el 8-9 20:57