17 de febrero de 2010

LINQ to XML, Modificar Archivo XML

Veamos mediante ejemplos prácticos como modificar un árbol XML con .NET (C#).

En este artículo veremos como adicionar, modificar o eliminar elementos y atributos en documentos XML, usando la nueva tecnología LINQ to XML.

Pero antes veamos el fichero XML con que vamos a trabajar:

 

Archivo XML a Leer

<?xml version="1.0" encoding="utf-8"?>
<Contactos>
  <Contacto>
    <Nombre>Juan</Nombre>
    <EMail>juan@gmail.com</EMail>
    <Telefono Tipo="Móvil">666666666</Telefono>
    <Telefono Tipo="Trabajo">911111111</Telefono>
  </Contacto>
  <Contacto>
    <Nombre>Luis</Nombre>
    <EMail>luis@gmail.com</EMail>
    <Telefono Tipo="Móvil">666777777</Telefono>
  </Contacto>
  <Contacto>
    <Nombre>María</Nombre>
    <EMail>maria@gmail.com</EMail>
    <Telefono Tipo="Móvil">666888888</Telefono>
    <Telefono Tipo="Personal">91222222</Telefono>
  </Contacto>
</Contactos>

 

Adicionar un elemento con LINQ to XML

Existen varias formas en .NET C# para crear un nuevo elemento en un árbol XML, acá expondremos 2 formas distintas:

  1. XElement.Add: Se describe por si solo, adiciona un nuevo elemento.
  2. XElement.SetElementValue: Modifica, agrega o elimina un elemento.

Como vemos en el contenido del archivo XML de arriba, nuestro segundo contacto (Luis) tiene un elemento EMail asociado, imaginemos que queremos modificar el Email. Veamos las 2 variantes de código para obtener dicho resultado:

Método XElement.Add

// Código para adicionar un elemento usando Add (LINQ 2 XML)
private static void Linq2XmlAddEmailUsandoAdd()
{
  XElement xmlContactos = XElement.Load("Contactos.xml");
 
  //Obtener el contacto con nombre Luis
  var contactoLuis =
     (from c in xmlContactos.Descendants("Contacto")
      where c.Element("Nombre").Value.ToUpper() == "LUIS"
      select c).FirstOrDefault();
 
  if (contactoLuis != null)
  {
    string strXml = @"<EMail>luis1@gmail.com</EMail>";
    contactoLuis.Add(XElement.Parse(strXml));
  }
 
}

Si usamos el código que hemos expuesto, en realidad no estamos modificando el Email, sino que estamos adicionando un nuevo Email, es decir para Luis tendríamos ahora 2 Mail, el que tenia anteriormente (luis@gmail.com) y el que recién acabamos de adicionar (luis1@gmail.com). Es decir que no logramos el resultado esperado. Para lograrlo teníamos que haber eliminado el nodo y crearlo nuevamente con el valor correcto.

Veamos ahora el código usando el segundo método:

Método XElement.SetElementValue

// Código para adicionar un elemento usando SetElementValue (LINQ 2 XML)
private static void Linq2XmlAddEmailUsandoSetElementValue()
{
  XElement xmlContactos = XElement.Load("Contactos.xml");
 
  //Obtener el contacto con nombre Luis
  var contactoLuis =
     (from c in xmlContactos.Descendants("Contacto")
      where c.Element("Nombre").Value.ToUpper() == "LUIS"
      select c).FirstOrDefault();
 
  if (contactoLuis != null)
    contactoLuis.SetElementValue("EMail", luis2@gmail.com);
}

Usando este segundo código, sí logramos el resultado esperado, porque el método SetElementValue lo que hace es buscar dentro del elemento contactoLuis, el nodo o elemento EMail y si existe modifica su valor, en caso contrario lo adiciona.

Si nuestro objetivo hubiese sido eliminar el nodo Email, podríamos haber usado el método  SetElementValue  con el segundo parámetro en null:

contactoLuis.SetElementValue("EMail", null);

 

XElement.SetAttributeValue

Al igual que el método SetElementValue, existe un método SetAttributeValue que establece, agrega o elimina un atributo de un elemento. Pero veamos un ejemplo donde le cambiemos el atributo del teléfono que tiene Luis (cambiarlo de Móvil a Trabajo):

// Código para modificar atributo usando SetAttributeValue (LINQ 2 XML)
private static void Linq2XmlSetAttributeValue()
{
  XElement xmlContactos = XElement.Load("Contactos.xml");
 
  //Obtener el contacto con nombre Luis
  var contactoLuis =
     (from c in xmlContactos.Descendants("Contacto")
      where c.Element("Nombre").Value.ToUpper() == "LUIS"
         && c.Element("Telefono").HasAttributes
      select c).FirstOrDefault();
 
  if (contactoLuis != null)
    contactoLuis.Element("Telefono").SetAttributeValue("Tipo", "Trabajo");
}

Nota: En el código anterior, en la consulta hemos preguntado por la propiedad HasAttributes, en realidad carece de sentido en nuestro ejemplo preguntar por dicha propiedad, porque si el elemento Telefono no tuviese atributos igual crearía el atributo Tipo. De todas formas me pareció interesante incorporar la propiedad para que el lector sepa de su existencia, en ocasiones suele ser de utilidad.

De similar manera que sucede con el SetElementValue, si quisiéramos eliminar un atributo usaríamos el método SetAttributeValue con el segundo parámetro nulo (null).

 

Artículos Relacionados: