Listado de elementos de un XML a .NET con XMLSerializer

Allá por el 2010, que se dice pronto, conté una forma súper elegante de volcar el contenido de un XML en un objeto en .NET utilizando XmlSerializer. Este fin de semana sufrimos un momento de frustración severa al no conseguir utilizar este mismo mecanismo para un listado de objetos del mismo tipo. Es por ello que hoy quiero compartir contigo la solución y que puedas disfrutar aún más de tus horas programando.

Para este ejemplo voy a utilizar el siguiente XML:

<?xml version="1.0" encoding="UTF-8"?>
<posts>
<post id="1">
<title>XMLSerializer y XML Attributes</title>
<tags>csharp, xml</tags>
<url>https://www.returngis.net/2010/05/xmlserializer-y-xml-attributes/</url>
</post>
<post id="2">
<title>Activa un segundo factor de autenticación en Linkedin</title>
<tags>Linkedin, Redes sociales, Seguridad</tags>
<url>https://www.returngis.net/2015/09/activa-el-segundo-de-factor-de-autenticacion-en-linkedin/</url>
</post>
<post id="3">
<title>Eliminar metadatos de archivos en Windows</title>
<tags>metadatos, Privacidad, Windows</tags>
<url>https://www.returngis.net/2015/09/eliminar-metadatos-de-archivos-en-windows/</url>
</post>
<post id="4">
<title>Si usas WhatsApp Web cierra la sesión cuando termines</title>
<tags>Seguridad, WhatsApp</tags>
<url>https://www.returngis.net/2015/09/si-usas-whatsapp-web-cierra-las-conexiones-cuando-termines/</url>
</post>
</posts>

Como puedes ver, contiene un listado de elementos de tipo post que, idealmente, deberían volcarse dentro de un listado de objetos del tipo, digamos, Post.

Como se vio en aquel artículo, la clase podría ser como la siguiente:

using System.Xml.Serialization;

namespace XMLSerializerListOfObjects
{
    public class Post
    {
        [XmlAttribute("id")]
        public int Id { get; set; }

        [XmlElement("title")]
        public string Title { get; set; }

        [XmlElement("tags")]
        public string Tags { get; set; }

        [XmlElement("url")]
        public string Url { get; set; }
    }
}

Para poder volcar el listado completo de todos los elementos que aparecen en el archivo, no podemos simplemente pasar a la clase XmlSerializer List<Posts>. La solución en este caso es replicar la estructura del XML, al menos con el elemento raíz que engloba estos artículos. Para ello he creado una nueva clase llamada Posts que encapsula todos los posts dentro del archivo XML:

using System.Collections.Generic;
using System.Xml.Serialization;

namespace XMLSerializerListOfObjects
{
    [XmlRoot("posts")]
    public class Posts
    {
        [XmlElement("post")]
        public List<Post> listOfPosts { get; set; }

    }
}

Como puedes ver en el código anterior, es importante marcar la misma como elemento root del XML y además indicar que listOfPosts es un XmlElement que corresponde con el elemento post, aunque sea un listado.

Por último, la forma de deserializar el XML del ejemplo sería utilizando como tipo la clase Posts.

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

namespace XMLSerializerListOfObjects
{
    class Program
    {
        static void Main(string[] args)
        {

            var xmlSerializer = new XmlSerializer(typeof(Posts));

            var reader = new StreamReader("test.xml");

            var posts = (Posts)xmlSerializer.Deserialize(reader);

            foreach (var post in posts.listOfPosts)
            {
                Console.WriteLine("Post #{0}\n Title: {1}\n Tags: {2}\n URL: {3}\n", post.Id, post.Title, post.Tags, post.Url);
            }

            Console.ReadLine();
        }
    }
}

¡Saludos!