Leer los atributos de un elemento XML

Si no habéis trabajado demasiado con la lectura de ficheros XML, antes de comenzar con este artículo, sería conveniente que os leyerais el artículo de introducción al XmlTextReader.

Lo que en este artículo se va a tratar es una de esas cositas sueltas de las que no se suele escribir porque se considera poco importante, pero que llegado el momento suele hacer falta saber: leer los atributos de un elemento XML.

Si bien es cierto que hay métodos del XmlTextReader que leen atributos, no es menos cierto que éstos suelen dar muchos problemas si no tenemos muy clara la estructura del XML, abocándonos a bucles infinitos o consiguiendo un null por respuesta.

Vayamos pues al grano. Imaginemos que tenemos este fichero XML:

<raiz>
    <persona nombre="Juan" apellidos="Martínez Martínez">   
    <persona nombre="Bill" apellidos="Gates">
    <persona nombre="Napoleón" apellidos="Buenaparte">    ...
</raiz>


Vamos a crear una función donde se utilizarán listas genéricas en las que se almacerán elementos de la clase Persona tras leer el XML completo.

1. crear la clase persona
Con motivo de que el artículo sea completo, creamos una sencilla clase persona con sólo dos propiedades: nombre y apellidos:

public class Persona
{
    private string _nombre;
    public string nombre
    {
        get { return _nombre; }
        set { _nombre = value; }
    }

    private string _apellidos;
    public string apellidos
    {
        get { return _apellidos; }
        set { _apellidos = value; }
    }
}


2.- ¡Recordad importar los namespace correspondientes!
Que son el namespace en el que encontramos todas las clases que manejan XML y el de las colecciones genéricas.

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


3.- Creamos el algoritmo de lectura
Veamos el código comentado:
    public List<Persona> leemosAtributos()
    {
        // Creamos la lista genérica de Personas
        List<Persona> personas = new List<Persona>();

        string url = "Path o Url de nuestro XML";
        // Abrimos el elemto XmlTextReader usando la sentencia using,
        // que se encargará de cerrar todos los recursos que use el objeto
        using (XmlTextReader reader = new XmlTextReader(url))
        {

            // Con estas dos sentencias, no situamos en el primer elemento
            // de nuestro fichero XML (raíz)
            reader.MoveToContent();
            reader.ReadStartElement();

            // Ahora viene el típico bucle del que sólo saldremos cuando se termine
            // el fichero XML
            while (reader.Read())
            {
                // Si el reader está en un elemento y ese elemento se llama "persona"
                // (en nuestro ejemplo todos los elementos se llaman persona)
                if ((reader.NodeType == XmlNodeType.Element) && (reader.Name == "persona"))
                {
                    // Creamos un objeto del tipo persona
                    Persona persona = new Persona();

                    // Aquí viene el alma de nuestro algoritmo
                    // Viajamos desde el primer al último atributo
                    for (int i = 0; i < reader.AttributeCount; i++)
                    {
                        // Nos movemos al atributo que nos corresponde.
                        // Es muy importante observar que siempre se mueve de los atributos previos a los
                        // posteriores, pues el XmlTextReader sólo sabe ir hacia delante.
                        reader.MoveToAttribute(i);
                        {
                            // Según sea el nombre del atributo, colocamos su valor en una u otra
                            // propiedad de nuestro objeto Persona
                            switch (reader.Name)
                            {
                                case "nombre":
                                    persona.nombre = reader.Value;
                                    break;
                                case "apellidos":
                                    persona.apellidos = reader.Value;
                                    break;
                            }
                        }
                    }

                    // Finalmente añadimos el objeto Persona al listado genérico
                    personas.Add(persona);
                }
            }
        }

        // Devolvemos el listado genérico
        return personas;
    }


Nota: el algoritmo propuesto es simplemente una base, en ningún momento se comprueba que los datos sean válidos o que existan; ni tal siquiera si el propio XML es válido.