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.
También te puede interesar
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 & 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 & 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.