C# – Leer archivo XML

Con LINQ to XML se carga el archivo, se piden los elementos que interesan y se convierten a su tipo en el mismo paso. Y si el elemento no está, devuelve null en lugar de reventar.

Sin namespace, sin class Program, sin Main. Los ejemplos usan instrucciones de nivel superior, disponibles desde C# 9: el compilador genera esa estructura por ti. Y desde .NET 10 un archivo suelto se ejecuta directamente con dotnet run archivo.cs, sin crear un proyecto. El código de siempre, con su static void Main, sigue funcionando igual. Tampoco hacen falta los using System;, System.IO y compañía: los proyectos modernos los incluyen de forma implícita.

El archivo

<?xml version="1.0" encoding="utf-8"?>
<empresa>
  <empleado id="1">
    <nombre>Ana &amp; Cía</nombre>
    <estatus>activo</estatus>
  </empleado>
  <empleado id="2">
    <nombre>Luis</nombre>
    <estatus>baja</estatus>
  </empleado>
</empresa>

Leerlo

using System.Xml.Linq;

var doc = XDocument.Load("empresa.xml");

foreach (var e in doc.Root!.Elements("empleado"))
    Console.WriteLine($"{(int)e.Attribute("id")!}: {(string?)e.Element("nombre")} ({(string?)e.Element("estatus")})");
salida1: Ana & Cía (activo)
2: Luis (baja)

El truco está en los casts: (int) sobre un atributo lo convierte a número, y (string?) sobre un elemento da su texto. Fíjate en que el &amp; del archivo vuelve a ser un & normal.

Lo que falta no revienta

Console.WriteLine((string?)doc.Root.Element("empleado")!.Element("telefono") ?? "(null)");
salida(null)

Si el elemento no existe, Element devuelve null, y el cast a string? también. Con .Value en lugar del cast, en cambio, saltaría una NullReferenceException. Por eso los casts son la forma idiomática.

Filtrar

var activos = doc.Descendants("empleado")
                 .Where(e => (string?)e.Element("estatus") == "activo")
                 .Select(e => (string?)e.Element("nombre"));

Console.WriteLine($"activos: {string.Join(", ", activos)}");
salidaactivos: Ana & Cía

El mismo recorrido con XmlDocument

using System.Xml;

var viejo = new XmlDocument();
viejo.Load("empresa.xml");

foreach (XmlNode nodo in viejo.DocumentElement!.ChildNodes)
    Console.WriteLine($"{nodo.Attributes!["id"]!.Value} -> {nodo["nombre"]!.InnerText}");
salida1 -> Ana & Cía
2 -> Luis

Es lo que usaba la versión anterior de esta página. Funciona, pero cuidado con ChildNodes: incluye todos los nodos hijos, también los comentarios y, según cómo se cargue, el texto en blanco entre elementos. Con un comentario dentro de <empresa>, ese bucle intentaría leerle un atributo id y fallaría. Elements("empleado") de LINQ to XML solo devuelve lo que pides.

XML mal formado

salidaXmlException: The 'b' start tag on line 1 position 5 does not match the end tag of 'a'. Line 1, position 9.

Todo el código se ejecutó con .NET 10 en un contenedor limpio antes de publicar esta página; las salidas están copiadas de esa ejecución.