This Blog

Syndication

Search

Tags

Community

Email Notifications

Archives

Enlaces Recomendados

LINQ To XML: Integración de XML!

Como no podía ser de otra forma, y después de hablar de las bases de LINQ y de LINQ To SQL, desde el CIIN nos faltaba comentar la “tercera pata” del proyecto LINQ: LINQ To XML. Lo de tercera pata va entrecomillado, porque ya tenemos a la vista nuevas capacidades (como LINQ To XSD) y nuevas siglas  que integran las siglas LINQ (CLINQ, para habilitar el uso de parte de las características de LINQ en C++/CLI, PLINQ, para definir una implementación de LINQ que permita la ejecución de código LINQ en múltiples CPU’s,  y seguro que algún componente más de LINQ sobre el que alguien ya está trabajando) y de las que espero poder comentaros cosas interesantes próximamente (especialmente de BLINQ).  Empecemos pues con LINQ To XML.

¿Qué es LINQ To XML?

Según la documentación oficial sobre LINQ, LINQ To XML es el componente específico de LINQ pensado para trabajar con información en formato XML cacheada en memoria, aprovechando las ventajas de los operadores estándar de consulta, y que además expone y añade nuevas funcionalidades para crear y manipular (a través de la integración en .NET Framework de toda la potencia que dan las transformaciones XPath y XQuery) de manera sencilla tanto documentos XML completos como porciones o fragmentos de una estructura XML. Por supuesto, como sucedía con LINQ To SQL, LINQ To XML va a permitir combinar consultas y transformaciones de documentos o fragmentos XML con consultas de otros orígenes de datos (relacionales o no). Estas prestaciones de LINQ To XML se apoyan en las siguientes características clave:

·         Las innovaciones del lenguaje (C# 3.0 y VB 9.0) que vimos en un post previo, así como en las características propias de LINQ (consultas integradas en el lenguaje).

·         Consultas a estructuras XML integradas en el lenguaje, es decir, tenemos la potencia de XPath y XQuery dentro de C# y VB.

·         Se sigue manteniendo la experiencia de DOM, pero se utiliza de un modo más sencillo:

o   Visión centrada en los elementos particulares de una estructura XML, frente a la visión anterior centrada en el documento.

o   Construcción funcional y a medida de estructuras XML. No necesito construir un documento XML completo, y puedo trabajar directamente con elementos XML al estilo XPath.

·         Las estructuras XML se crean de un modo dinámico, lo que simplifica su tratamiento.

·         Supone una representación en memoria más eficiente, que se integra con la infraestructura de lectura/escritura de System.Xml.

·         Se pueden cargar documentos XML existentes desde múltiples orígenes: XmlReader, TextReder, o  un simple String.

Siguiendo la misma referencia de otros post,  en la traducción de El Proyecto LINQ de Octavio Hernández, LINQ To XML define tres tipos de objetos en los que se basa la mayor parte de la integración de consulta contra estructuras XML.

·         XName, que habilita los mecanismos para trabajar de manera sencilla con los identificadores de espacios de nombres (QNames) utilizados en los nombres de elementos y atributos.

·         XElement, que viene a representar un elemento o nodo de una estructura / documento XML.

·         XAttribute, que es la representación de un atributo de un elemento.

Aunque estos tres tipos son las piezas claves de LINQ To XML, hay muchas más clases definidas en su jerarquía de clases (hasta la Preview de Mayo de 2006).

Después de esta introducción a lo que es LINQ To XML, en los siguientes apartados trataré de cubrir aspectos relativos a la construcción de estructuras XML, la integración con datos precedentes de fuentes no XML, y operaciones de manipulación y transformación de estructuras XML con LINQ To XML. Empecemos.

Nota: Los requisitos necesarios para probar LINQ To SQL son los que ya comentamos en el primer post de LINQ.

Creación y consulta de información en estructuras XML

Lo primero que vamos a ver es como de un modo sencillo podemos crear un sencillo documento XML (utilizando lo que LINQ To XML denomina Construcción Funcional, que permite crear un estructura XML, completa o no, en una única sentencia), y como también es realmente sencillo definir una consulta sobre dicha estructura (que no deja de ser un objeto en memoria fácilmente consultable con LINQ). El código necesario para crear el documento XML es el siguiente:

           XDocument DocXML= new XDocument(

                    new XDeclaration("1.0","utf-8","yes"),

                    new XComment("Ejemplo de doc. generado con LINQ To XML"),

                            new XElement("Agenda",

                                new XElement("Contactos",

                                    new XElement("Contacto",new XAttribute("Categoría","Particular"),

                                    new XElement("Nombre","José González")),

                                    new XElement("Contacto",new XAttribute("Categoría","Particular"),

                                    new XElement("Nombre","María Méndez")),

                                    new XElement("Contacto",new XAttribute("Categoría","Trabajo"),

                                    new XElement("Nombre","Antonio Muñoz")))));

            Console.WriteLine(DocXML);

Con el código anterior, estamos creando un documento XML a partir de instanciar el objeto XDocument a través de una de las cuatro sobrecargas que tiene definidas. En concreto, la sobrecarga utilizada permite especificar mediante XDeclaration los valores utilizados para la versión, codificación y si el documento es standalone, y luego una lista de parámetros con los elementos que forman parte del documento XML y que en este caso son unos comentarios (especificados a través de XComment) y una jerarquía de elementos XML especificados mediante XElement. Finalmente, para visualizar por pantalla el documento vemos que no se necesita ningún tipo de factoría DOM, sino que es directamente visualizable sin hacer ningún tratamiento previo (lo que es una gran ventaja, ya que estamos creando un documento XML de un modo realmente sencillo con LINQ To XML). La salida por pantalla es la siguiente:

Al esquema anterior le podemos añadir de manera sencilla un nombre XML utilizando el objeto XNameSpace, que encapsula un espacio de nombres XML. Además, este documento no solo lo podemos visualizar de manera sencilla, sino que también guardarlo en disco realmente fácil utilizando el método Save() del objeto XDocument.

            XNamespace miEspacioNombres="http://www.ciin.es/esquemas";

            XDocument DocXML= new XDocument(

                    new XDeclaration("1.0","utf-8","yes"),

                    new XComment("Ejemplo de doc. generado con LINQ To XML"),

                            new XElement(miEspacioNombres + "Agenda",

                                new XElement(miEspacioNombres + "Contactos",

                                    new XElement(miEspacioNombres + "Contacto",

                                        new XAttribute("Categoría","Particular"),

                                    new XElement(miEspacioNombres + "Nombre","José González")),

                                    new XElement(miEspacioNombres + "Contacto",

                                         new XAttribute("Categoría","Particular"),

                                    new XElement(miEspacioNombres + "Nombre","María Méndez")),

                                    new XElement(miEspacioNombres + "Contacto",

                                        new XAttribute("Categoría","Trabajo"),

                                    new XElement(miEspacioNombres + "Nombre","Antonio Muñoz")))));

 

            Console.WriteLine(DocXML.ToString());

            DocXML.Save("Contactos.xml");

 En este caso, el documento XML guardado en disco tendría este aspecto:

Nota: Igual que con el método Save() podemos guardar una estructura XML en disco, el método Load() de XDocument nos permite cargar un documento que está guardado en disco y manipularlo del mismo modo en que estamos trabajando con estructuras XML creadas en memoria.

Definir consultas sobre un documento XML como el anterior es bastante sencillo a través del uso de los operadores estándar de consulta. A modo de ejemplo, el siguiente código nos permite obtener todos los contactos de tipo Particular en el documento XML creado anteriormente:

         Console.WriteLine("****Definiendo una consulta contra una estructura XML****");

            var ContactosParticulares=

                from c in DocXML.Descendants(miEspacioNombres + "Contacto")

                where (string)c.Attribute("Categoría")=="Particular"

                orderby (string)c.Element(miEspacioNombres + "Nombre")

                select c;

            //Hacemos efectiva la consulta

            foreach (var contacto in ContactosParticulares)

            {

                Console.WriteLine("{0}",contacto);               

            }

 Del código anterior, hay que destacar un par de cosas:

·         EL método Descendant que nos permite obtener todos los elementos descendientes del tipo especificado (en este caso Contacto).

·         El hecho que tanto en la condición de la cláusula where como en la de la cláusula orderby estamos indicado que de manera específica se trabaja con elementos del documento XML de tipo cadena. Y la razón de este modo de trabajo hay que buscarla en el hecho de que LINQ To XML permite tratar los valores contenidos en elementos y atributos respetando sus tipos sin más que hacer un simple cast (aquí tenemos otra ventaja de LINTO To XML, frente al W3C DOM que siempre trata los nodos XML como textos).

La correspondiente salida por pantalla es la siguiente:

En esta salida vemos como efectivamente LINQ To XML no obliga a trabajar a nivel de documento XML completo, sino que podemos quedarnos a nivel de elementos XML.

Integración con datos no XML

Hasta ahora hemos visto como es bastante sencillo construir estructuras XML utilizando la Construcción Funcional de LINQ To XML. Sin embargo, las estructuras que hemos construido se basan en ir añadiendo de manera estática elementos  de tipo string al árbol. Como comentamos, se podrían añadir otros tipos de datos, y por supuesto, podemos utilizar variables en la construcción en lugar de valores. Pero, ¿Cómo hacemos más dinámica esta construcción funcional? La respuesta está de nuevo en utilizar el tipo IEnumerable<T> aplicada a objetos de tipo XElement, lo que nos permitirá construir estructuras XML a partir de objetos de memoria, resultados procedentes de hacer una consulta a una BD, etc. Para explicar este tipo de construcción, vamos a ver cómo podemos crear una estructura XML a partir de definir una consulta sobre un array de objetos de un cierto tipo:

·         Lo primero es definir la clase a partir de la cuál crearemos los objetos contenidos en el array.

 

        class Contacto

        {        

            public string Nombre;

            public string Telefono;          

        }

 

·         Lo siguiente que haremos es construir el arreglo de objetos aprovechándonos de algunas de las innovaciones en el lenguaje que ya vimos (en concreto, declaración implícita de tipos, tipos  anónimos e inicializadores de objetos):

 

            var contactos_agenda=new[]{

                new Contacto{Nombre="José González",Telefono="11111"},

                new Contacto{Nombre="Luis García", Telefono="22222"},

              

            };

  

·         Sin más, ya podemos construir una estructura XML (en este caso no vamos a construir un documento completo, sino sólo una serie de elementos XML) a partir de definir una consulta XML sobre el array de objetos creado:

            XElement Contactos=

                new XElement(miEspacioNombres + "Contactos",

                    from c in contactos_agenda

                    select new XElement(miEspacioNombres + "Contacto",

                        new XElement(miEspacioNombres + "Nombre",c.Nombre),

                        new XElement(miEspacioNombres + "Telefono",c.Telefono)));

            Console.WriteLine(Contactos.ToString());

 La clave del código anterior está en que dentro de la cláusula Select podemos construir objetos de tipo XElement, y en que los objetos de tipo XElement pueden ser construidos a partir del resultado de una consulta a un objeto de memoria. La salida por pantalla generada es la siguiente:

Igual que hemos construido una estructura XML a partir de definir una consulta contra un objeto de memoria, se aplicaría la misma filosofía y método para generar estructuras / documentos XML a partir de datos de una BD relacional y apoyándonos en LINQ To SQL como ya vimos.

Operación y manipulación de estructuras XML

Como comentábamos, otra característica relevante de LINQ To XML es que permite realizar de un modo sencillo operaciones con estructura XML. Vamos a ver algunas de ellas:

·         Operaciones de Transformación, que nos permiten transformar y extender estructuras XML ya definidas. A partir de los elementos de una estructura XML existente, es posible crear nuevos elementos:

 

Console.WriteLine("***********Operaciones de Transformación***********");          

 XElement ElementoTransformado =

                new XElement (miEspacioNombres +  "Amigos",

                    from c in ContactosParticulares

                    select new XElement(miEspacioNombres + "Amigo",

                                new XAttribute("NombreCompleto",(string)c.Element(miEspacioNombres +  

                                                         "Nombre"))));

            Console.WriteLine("\n\n {0}",ElementoTransformado);

 

Lo que hemos hecho en el código anterior de la consulta de los contactos particulares del documento original XML para generar una estructura XML distinta, pero aprovechando datos de la consulta. La salida por pantalla obtenida es la siguiente:

 

·         Añadir, eliminar y actualizar elementos, LINQ To XML permite añadir, eliminar o modificar contenidos en una estructura XML a partir de los métodos Add(), Remove() y ReplaceContent() del objeto XElement. Así, para modificar la estructura anterior el código necesario sería:

            Console.WriteLine("***********Añadir, Borrar, Modificar elementos**********");

            XElement Telefono=new XElement(miEspacioNombres  + "Telefono","111111");

            ElementoTransformado.Element(miEspacioNombres + "Amigo").Add(Telefono);

            Console.WriteLine("\n Añadir");

            Console.WriteLine("\n {0}",ElementoTransformado);

           

            Console.WriteLine("\n Modificar");

            ElementoTransformado.Element(miEspacioNombres  +

                               "Amigo").Element(miEspacioNombres  +

                              "Telefono").ReplaceContent("22222");

            Console.WriteLine("\n {0}",ElementoTransformado);

 

            Console.WriteLine("Borrar");

            ElementoTransformado.Element(miEspacioNombres  +

                             "Amigo").Element(miEspacioNombres  + "Telefono").Remove();

            Console.WriteLine("\n {0}",ElementoTransformado);

 Y la salida por pantalla correspondiente es la siguiente:

·         Navegar por los nodos de una estructura XML, LINQ To XML habilita varios mecanismos para navegar por los nodos de una estructura XML a través de los métodos Nodes() y Descendant() del objeto XElement. Estos métodos nos facilitan la tarea de poder trabajar con elementos concretos de nuestra estructura XML. Un ejemplo de uso de ambos es el siguiente:

            Console.WriteLine("****Navegando por los nodos de un elemento XML****");

            Console.WriteLine("\n Con Descendants()");

            foreach(var x in ElementoTransformado.Descendants(miEspacioNombres +  "Amigo")){

                Console.WriteLine(x);

            }

 

            Console.WriteLine("\n Con Nodes()");

           foreach(var x in ElementoTransformado.Nodes()){

                Console.WriteLine(x);

            }

 La salida por pantalla que se obtiene es la siguiente:

Como vemos, la salida que producen ambos métodos es la misma para el caso de una estructura XML sencilla. Para estructuras más complicadas, el resultado será distinto puesto que Descendants() siempre nos devolverá todos los elementos por debajo de  un elemento especificado, mientras que Nodes() siempre nos va a devolver los nodos por debajo del primer elemento en la jerarquía de la estructura XML en cuestión.

Nota: En el caso de trabajar con atributos, sólo es posible realizar operaciones de borrado de los mismos a través de los método Remove() o SetAttribute() del objeto XAttribute.

Finalmente,  y para concluir el post os dejo una recopilación de las extensiones específicas de LINQ To XML que proporcionan la capacidad para realizar ciertas operaciones que tienen sentido al trabajar con estructuras XML (algunas de ellas las hemos visto en las operaciones de manipulación y transformación de estructuras XML).

Operadores

Descripción

Elements()

Devuelve todos los elementos hijos para cada XElement de una secuencia de XElements (IEnumerable <XElement>).

Descendants() y Ancestors()

Nos permiten consultar respectivamente los elementos situados por encima y por debajo (en la jerarquía) del elemento especificado (sin incluirlo).

Attributes()

Es una extensión llamada en IEnumerable<XElement> que devuelve una secuencia de atributos (IEnumerable<Attributes>).

ElementsBeforeThis, ElementsAftherThis

Permiten devolver todos los elementos hijos o contenidos que aparecen, respectivamente, antes y después de un elemento particular. En ambos casos devuelven un tipo IEnumerable<XElement>.

NodesBeforeThis, NodesAfterThis

Se comportan igual que ElementsBeforeThis y ElementsAfterThis, salvo que en este caso se devuelve un tipo IEnumerabe<XNode>.

Y hasta aquí lo que os quería contar de LINQ To XML. En la próxima me gustaría comentaros otras iniciativas que están surgiendo en torno a LINQ, y enseñaros con ejemplos de que va BLINQ. Espero que el post os haya resultado de interés. Como en el post anterior, os dejo aquí el zip con la solución del proyecto de LINQ To XML.

Published 7/3/2007 17:39 por Juan Carlos González Martín

Archivado en:
Comparte este post:

Comentarios

# Visual Studio 2008 Beta2: Realizando Consultas con LINQ (I) &laquo;@ Thursday, February 28, 2008 11:22 AM

PingBack desde  Visual Studio 2008 Beta2: Realizando Consultas con LINQ (I) &laquo;

Visual Studio 2008 Beta2: Realizando Consultas con LINQ (I) «

# Extensibilidad de LINQ: LINQ Flavours! &laquo; Pasi??n por la tecnolog??a&#8230;@ Sunday, March 2, 2008 1:02 PM

PingBack desde  Extensibilidad de LINQ: LINQ Flavours! &laquo; Pasi??n por la tecnolog??a&#8230;

Extensibilidad de LINQ: LINQ Flavours! « Pasi??n por la tecnolog??a…

# re: LINQ To XML: Integración de XML!@ Sunday, October 5, 2008 12:43 AM

Hola! Buen articulo.

Quisiera saber si puedo mostrarlo por pantalla, pero sin etiquetas

Saludos

Jimmy

# re: LINQ To XML: Integración de XML!@ Monday, November 22, 2010 10:34 PM

Me podrias ayudar tengo que meter en un xml que hice una referencia de apoyo que aparezca como xsi:schemaLocation="unaUrl" y un xmlns:xsi="www.w3.org/.../XMLSchema-instance"

slalo