Capitulo 5 - LINQ to XML (Parte 2)


Lección 2: Consultando con LINQ to XML
La familia XDocument
En la búsqueda de diseñar una manera de utilizar LINQ sobre XML de datos, Microsoft creó las clases XDocument: XElement, XAttribute, XNamespace, XDirective, y mucho más. Para utilizar estas clases, se debe agregar una referencia al assembly System.Xml.Linq.dll, y agregar una importación (C # usando) System.Xml.Linq en el código.
La clase XObject
La mayoría de las clases XDocument derivan de la clase XObject:
■ BaseUri: una propiedad de sólo lectura que contiene la cadena URI base para el objeto actual.
■ Document: una propiedad de sólo lectura que hace referencia al XDocument al que pertenece el objeto actual.
■ NodeType: una propiedad de sólo lectura que devuelve XmlNodeType del nodo actual.
■ Parent: una propiedad de sólo lectura que devuelve a los padres XElement del nodo actual.
■AddAnnotation: método para agregar un objeto de anotación (similar a un archivo adjunto) a la lista de anotaciones del objeto.
■ Annotation: devuelven la primera anotación del tipo pasado a este método como un parámetro.
■ Annotations:devuelven una colección del tipo pasado a este método como un parámetro.
■ RemoveAnnotations: elimina las anotaciones del tipo pasado a este método como un parámetro.
■ Changed: evento que se produce cuando la clase XObject actual o cualquiera de sus descendientes han cambiado.
■ Changing: evento que se produce antes de la clase XObject actual o cualquiera de susdescendientes cambia.

Además de estos miembros, la clase XObject explícitamente implementa la interfaz IXmlLineInfo, que contiene y las propiedades LineNumber LinePosition y el método HasLineInfo.
La clase XAttribute
Representa a un atributo XML. La clase XAttribute se deriva de la clase XObject, por lo que hereda todos los miembros de la clase XObject y además contiene los siguientes miembros:
■EmptySequence: propiedad de sólo lectura que devuelve un IEnumerable de XAttribute vacíos .
■ IsNameSpaceDeclaration: una propiedad de sólo lectura que devuelve true si el objeto XAttribute actual representa un identificador de espacio de nombres.
■ Name: propiedad de sólo lectura que devuelve el nombre del atributo XML como una cadena.
■ NextAttribute: propiedad de sólo lectura que devuelve una referencia a la siguiente atributo XML. Devuelve null si el objeto XAttribute actual no tiene padre o si el objeto XAttribute actual es el último atributo XML de los padres.
■ NodeType Reemplaza la propiedad heredada NodeType de XObject. Devuelve un XmlNodeType.XAttribute.
■ PreviousAttribute propiedad de sólo lectura que devuelve una referencia a la anterior atributo XML.
■ Value: propiedad de cadena que se obtiene o establece el valor del atributo XML.
■ Operator:  25 operadores de conversión explícita que le permiten convertir el objeto XAttribute a muchos tipos usando CType basado en el valor del atributo XML. Internamente, esto utiliza la clase XmlConvert para convertir el valor del atributo XML al tipo deseado.
■ Remove:  elimina el objeto XAttribute actual del elemento padre. Se produce una excepción InvalidOperationException si el objeto XAttribute actual no tiene padres.
■ SetValue: método que asigna una cadena de valor al XAttribute actual.
■ ToString:  método que muestra el nombre y el valor del atributo XML .
 
La Clase XNode
Ademas de todos los miembros heredados de XObject, la clase XNode contiene los siguientes miembros:
Nombre
Descripción
compara la posición relativa de dos nodos.
comprueba si los valores de dos nodos son iguales.
Obtiene el siguiente nodo relacionado de este nodo.
Obtiene el anterior nodo relacionado de este nodo.
Agrega el contenido especificado inmediatamente a continuación de este nodo.
Agrega el contenido especificado inmediatamente antes de este nodo.
Devuelve una colección de los elementos antecesores de este nodo.
Compara dos nodos para determinar el orden relativo de sus documentos XML.
Crea un objeto XmlReader para este nodo.
Compara los valores de dos nodos, incluidos los valores de todos los nodos descendientes.
Devuelve una colección de los elementos relacionados situados detrás de este nodo en el orden del documento.
Devuelve una colección de los elementos relacionados situados antes de este nodo en el orden del documento.
Determina si el nodo actual aparece después de un nodo especificado respecto al orden del documento.
Determina si el nodo actual aparece antes de un nodo especificado respecto al orden del documento.
Crea una copia superficial del objeto Object actual. (Se hereda de Object).
Devuelve una colección de los nodos relacionados situados detrás de este nodo en el orden del documento.
Devuelve una colección de los nodos relacionados situados antes de este nodo en el orden del documento.
Crea un objeto XNode a partir de un objeto XmlReader.
Quita este nodo de su elemento primario.
Reemplaza este nodo por el contenido especificado.
Devuelve el XML con sangría para este nodo. (Invalida a Object.ToString).
Escribe este nodo en un objeto XmlWriter.
La Clase XContainer
Representa un nodo que puede contener otros nodos, y es un MustInherit (C # abstract) que deriva de la clase XNode:
Nombre
Descripción
Obtiene el primer nodo secundario de este nodo.
Obtiene el último nodo secundario de este nodo.
Agrega el contenido especificado como elementos secundarios de este XContainer.
Agrega el contenido especificado como el primer elemento secundario de este documento o elemento.
Crea un objeto XmlWriter que se puede utilizar para agregar los nodos al XContainer.
Devuelve una colección de los nodos descendientes de este documento o elemento, clasificados por documento.
Devuelve una colección de los elementos descendientes de este documento o elemento, clasificados por documento.
Obtiene el primer elemento secundario (clasificado por documento) con el XName especificado.
Devuelve una colección de los elementos secundarios de este elemento o documento, clasificados por documento.
Devuelve una colección de los nodos secundarios de este elemento o documento, clasificados por documento.
Quita los nodos secundarios de este documento o elemento.
Reemplaza los nodos secundarios de este documento o elemento por el contenido especificado.
La clase XElement XElement representa un elemento XML, se compone de un nombre local y el espacio de nombres. Opcionalmente, los XElement pueden contener atributos XML (XAttribute):
Nombre
Descripción
Obtiene una colección de elementos vacía.
Obtiene el primer atributo de este elemento.
Obtiene un valor que indica si este elemento tiene al menos un atributo.
Obtiene un valor que indica si este elemento tiene al menos un elemento secundario.
Obtiene un valor que indica si este elemento no incluye ningún contenido.
Obtiene el último atributo de este elemento.
Obtiene o establece el nombre de este elemento.
Obtiene el tipo de nodo de este nodo. (Invalida a XObject.NodeType).
Obtiene o establece el contenido de texto concatenado de este elemento.
Devuelve una colección de elementos que contienen este elemento y sus antecesores.
Devuelve el XAttribute de XElement que tiene el XName especificado.
Devuelve una colección de los atributos de este elemento.
Devuelve una colección de nodos que contienen este elemento y todos sus nodos descendientes, en el orden del documento.
Devuelve la colección de los elementos que contienen este elemento y todos sus elementos descendientes, en el orden del documento.
Obtiene el objeto XNamespace predeterminado de este XElement.
Obtiene el espacio de nombres asociado a un prefijo en particular para XElement.
Obtiene el prefijo asociado a un espacio de nombres correspondiente a este XElement.
Crea una nueva instancia de XElement usando la secuencia especificada, desde un archivo, desde TextReader o desde XmlReader
Cargua un XElement a partir de una cadena que contiene el XML.
Quita nodos y atributos de este XElement.
Quita los atributos de este XElement.
Reemplaza los nodos secundarios y los atributos de este elemento por el contenido especificado.
Reemplaza los atributos de este elemento por el contenido especificado.
Genera este XElement en el objeto Stream especificado, un archivo, un objeto TextWriter o un objeto XmlWriter .
Establece el valor de un elemento secundario, agrega un elemento secundario o lo quita.
Establece el valor de este elemento.
Escribe el elemento en un objeto XmlWriter. (Invalida a XNode.WriteTo(XmlWriter)).
La clase XDocument Representa un documento XML que puede contener una DTD, un elemento raíz XML, los comentarios XML cero-a-muchos, y de cero a muchas instrucciones de procesamientoXML. La clase XDocument deriva de XContainer y por lo tanto hereda los miembros de XContainer, XNode y XObject. Los siguientes son los miembros de la clase XDocument:
Nombre
Descripción
Obtiene o establece la declaración XML de este documento.
Obtiene la definición de tipo de documento (DTD) de este documento.
Obtiene el elemento raíz del árbol XML de este documento.
Crea una nueva instancia de XDocument usando la secuencia especificada, a partir de un archivo, de un TextReader, de un XmlReader.
Crea un nuevo XDocument a partir de una cadena.
Genera este XDocument en el objeto Stream especificado, en un archivo, (sobrescribiendo un archivo existente, si existe), en un TextWriter, en un XmlWriter.
Escribe el documento en un objeto XmlWriter. (Invalida a XNode.WriteTo(XmlWriter)).
Utilizar las clases XDocument La clase XDocument se puede llenar con facilidad utilizando el constructor, el método Load o el método Parse. El siguiente ejemplo de código utiliza el método Parse para cargar una cadena XML en un objeto XDocument. Después de eso, el método Saveguarda el documento XML en el archivo de XDocumentTest.xml.
-------------------------VB-------------------------------
Dim xml = _
"<CustomersOrders>" & _
" <Customer CustomerID='ALFKI' CompanyName='Alfreds Futterkiste'>" & _
" <Order OrderID='10643' Freight='29.4600'/>" & _
" <Order OrderID='10692' Freight='61.0200'/>" & _
" </Customer>" & _
" <Customer CustomerID='ANATR' CompanyName='Ana Trujillo'>" & _
" <Order OrderID='10308' Freight='1.6100'/>" & _
" <Order OrderID='10625' Freight='43.9000'/>" & _
" </Customer>" & _
" <Customer CustomerID='ANTON' CompanyName='Antonio Moreno'>" & _
" <Order OrderID='10365' Freight='22.0000'/>" & _
" <Order OrderID='10507' Freight='47.4500'/>" & _
" </Customer>" & _
" <Customer CustomerID='AROUT' CompanyName='Around the Horn'>" & _
" <Order OrderID='10355' Freight='41.9500'/>" & _
" <Order OrderID='10383' Freight='34.2400'/>" & _
" </Customer>" & _
"</CustomersOrders>"
Dim doc = XDocument.Parse(xml)
doc.Save(getFilePath("XDocumentTest.xml"))

--------------------------------CS-------------------------------------

string xml = @"
<CustomersOrders>
<Customer CustomerID='ALFKI' CompanyName='Alfreds Futterkiste'>
<Order OrderID='10643' Freight='29.4600' />
<Order OrderID='10692' Freight='61.0200' />
</Customer>
<Customer CustomerID='ANATR' CompanyName='Ana Trujillo'>
<Order OrderID='10308' Freight='1.6100' />
<Order OrderID='10625' Freight='43.9000' />
</Customer>
<Customer CustomerID='ANTON' CompanyName='Antonio Moreno'>
<Order OrderID='10365' Freight='22.0000' />
<Order OrderID='10507' Freight='47.4500' />
</Customer>
<Customer CustomerID='AROUT' CompanyName='Around the Horn'>
<Order OrderID='10355' Freight='41.9500' />
<Order OrderID='10383' Freight='34.2400' />
</Customer>
</CustomersOrders>";
var doc = XDocument.Parse(xml);
doc.Save(getFilePath("XDocumentTest.xml"));

Visual Basic tiene una forma alternativa de carga XML en un XDocument, como se muestra en el siguiente ejemplo de código, que implementa el constructor XDocument:


--------------------------------VB-------------------------------------
Dim doc As XDocument = _
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<CustomersOrders>
<Customer CustomerID='ALFKI' CompanyName='Alfreds Futterkiste'>
<Order OrderID='10643' Freight='29.4600'/>
<Order OrderID='10692' Freight='61.0200'/>
</Customer>
<Customer CustomerID='ANATR' CompanyName='Ana Trujillo'>
<Order OrderID='10308' Freight='1.6100'/>
<Order OrderID='10625' Freight='43.9000'/>
</Customer>
<Customer CustomerID='ANTON' CompanyName='Antonio Moreno'>
<Order OrderID='10365' Freight='22.0000'/>
<Order OrderID='10507' Freight='47.4500'/>
</Customer>
<Customer CustomerID='AROUT' CompanyName='Around the Horn'>
<Order OrderID='10355' Freight='41.9500'/>
<Order OrderID='10383' Freight='34.2400'/>
</Customer>
</CustomersOrders>
doc.Save(getFilePath("XDocumentTest.xml"))

--------------------------------CS-------------------------------------
var doc = new XDocument(
new XElement("CustomersOrders",
new XElement("Customer",
new XAttribute("CustomerID", "ALFKI"),
new XAttribute("CompanyName", "Alfreds Futterkiste"),
new XElement("Order",
new XAttribute("OrderID", "10643"),
new XAttribute("Freight", "29.4600")),
new XElement("Order",
new XAttribute("OrderID", "10692"),
new XAttribute("Freight", "61.0200")),
new XElement("Customer",
new XAttribute("CustomerID", "ANATR"),
new XAttribute("CompanyName", "Ana Trujillo"),
new XElement("Order",
new XAttribute("OrderID", "10308"),
new XAttribute("Freight", "1.6100")),
new XElement("Order",
new XAttribute("OrderID", "10625"),
new XAttribute("Freight", "43.9000")),
new XElement("Customer",
new XAttribute("CustomerID", "ANTON"),
new XAttribute("CompanyName", "Antonio Moreno"),
new XElement("Order",
new XAttribute("OrderID", "10365"),
new XAttribute("Freight", "22.0000")),
new XElement("Order",
new XAttribute("OrderID", "10507"),
new XAttribute("Freight", "47.4500")),
new XElement("Customer",
new XAttribute("CustomerID", "AROUT"),
new XAttribute("CompanyName", "Around the Horn"),
new XElement("Order",
new XAttribute("OrderID", "10355"),
new XAttribute("Freight", "41.9500")),
new XElement("Order",
new XAttribute("OrderID", "10383"),
new XAttribute("Freight", "34.2400"))))));
doc.Save(getFilePath("XDocumentTest.xml"));


En este ejemplo de código, el código de Visual Basic es mucho más simple que el códigoC #. El compilador de Visual Basic analiza los literales XML y genera el mismo código que el compilador de C # crea.


Implementando consultas de LINQ to XML


Se pueden utilizar muchas de las técnicas explicadas en el capítulo 3, "Introducción a LINQ," para consultar un XDocument o XElement:


--------------------------------VB-------------------------------------
Dim doc = XDocument.Load(getFilePath("XDocumentTest.xml"))
Dim result = (From order In doc.Descendants("Order")
Where order.Attribute("OrderID").Value = "10677"
Select New With
{
.OrderID = CType(order.Attribute("OrderID"), Integer),
.CustomerID = CType(order.Parent.Attribute("CustomerID"), String),
.Freight = CType(order.Attribute("Freight"), Decimal)
}).FirstOrDefault()
txtLog.Text = String.Format("OrderID:{0} CustomerID:{1} Freigth:{2:C}", _
result.OrderID, result.CustomerID, result.Freight)
End Sub

--------------------------------CS-------------------------------------
var doc = XDocument.Load(getFilePath("XDocumentTest.xml"));
var result = (from order in doc.Descendants("Order")
where order.Attribute("OrderID").Value == "10677"
select new
{
OrderID=(int)order.Attribute("OrderID"),
CustomerID = (string)order.Parent.Attribute("CustomerID"),
Freight = (decimal)order.Attribute("Freight")
}).FirstOrDefault();
txtLog.Text = string.Format("OrderID:{0} CustomerID:{1} Freigth:{2:C}",
result.OrderID, result.CustomerID, result.Freight);


En este ejemplo de código, el método de carga de la clase XDocument se ejecuta para recuperar el documento XML a partir de los ejemplos anteriores, y luego de una consultaLINQ se aplica al objeto XDocument. La cláusula select crea un tipo anónimo que contiene OrderID, CustomerID y Freight.


Usando LINQ to XML con agregados

Si desea consultar por la suma de la carga para cada cliente, puede utilizar LINQ to XMLcomo se muestra en el ejemplo de código:

--------------------------------VB-------------------------------------
Dim doc = XDocument.Load(getFilePath("XDocumentTest.xml"))
Dim result = From customer In doc.Descendants("Customer")
Select New With
{
.CustomerID = CType(customer.Attribute("CustomerID"), String),
.TotalFreight = customer.Descendants("Order") _
.Sum(Function(o) CType(o.Attribute("Freight"), Decimal))
}

--------------------------------CS-------------------------------------
var doc = XDocument.Load(getFilePath("XDocumentTest.xml"));
var result = from customer in doc.Descendants("Customer")
select new
{
CustomerID = (string)customer.Attribute("CustomerID"),
TotalFreight = customer.Descendants("Order")
.Sum(o=>(decimal)o.Attribute("Freight"))
};

Usando LINQ to XML Los Joins

Los Joins se pueden realizar entre LINQ to XML y otros proveedores de LINQ, como LINQ to Objects.

--------------------------------VB-------------------------------------
Dim orders() = {"10707", "10835", "10953"}
Dim doc = XDocument.Load(getFilePath("XDocumentTest.xml"))
Dim result = From order In doc.Descendants("Order")
Join selected In orders On
CType(order.Attribute("OrderID"), String) Equals selected
Select New With
{
.OrderID = CType(order.Attribute("OrderID"), Integer),
.CustomerID = CType(order.Parent.Attribute("CustomerID"), String),
.Freight = CType(order.Attribute("Freight"), Decimal)
}

--------------------------------CS-------------------------------------
string[] orders = {"10707","10835","10953"};
var doc = XDocument.Load(getFilePath("XDocumentTest.xml"));
var result = from order in doc.Descendants("Order")
join selected in orders
on (string) order.Attribute("OrderID") equals selected
select new
{
OrderID = (int) order.Attribute("OrderID"),
CustomerID = (string) order.Parent.Attribute("CustomerID"),
Freight = (decimal) order.Attribute("Freight")
};




Usando LINQ to XML con espacios de nombres

LINQ to XML es compatible con el uso de los espacios de nombres, también conocido como el URI de espacio, además del nombre local de un nodo XML.Los espacios de nombres XML se usan para evitar conflictos de nombres, sobre todo si se combinan varios documentos XML que puede haber nodos que tienen el mismo nombre pero con diferentes significados.


Cuando se trabaja con espacios de nombres XML, se puede asignar un prefijo al espacio de nombres. Los prefijos pueden ser la fuente de muchos problemas porque los prefijos están en el ámbito de su contexto, por lo que un prefijo abc puede estar asociada con espacio de nombres en varias partes del documento XML:


-------------------------------VB-------------------------------------
Private Sub LINQQueryNamespaceToolStripMenuItem_Click( _
ByVal sender As System.Object, ByVal e As System.EventArgs) _
Handles LINQQueryNamespaceToolStripMenuItem.Click
Dim xml = "<Root xmlns:aw='http://www.adventure-works.com'" & _
"
xmlns='http://www.xyz.com'>" & _
"
<Child>1</Child>" & _
"
<aw:Child>2</aw:Child>" & _
"
<Child>3</Child>" & _
"
<aw:Child>4</aw:Child>" & _
"
<Child>5</Child>" & _
"
<aw:Child>6</aw:Child>" & _
"
</Root>"
Dim doc = XDocument.Parse(xml)
txtLog.Clear()


Dim result1 = From c In doc.Descendants("Child")
Select c
txtLog.AppendText("
Query for Child\r\n")
For Each xElement In result1
txtLog.AppendText(
CType(xElement, String) + vbCrLf)
Next
Dim aw = XNamespace.Get("http://www.adventure-works.com")
Dim result2 = From c In doc.Descendants(aw + "Child")
Select c

txtLog.AppendText("
Query for aw+Child" + vbCrLf)
For Each xElement In result2

txtLog.AppendText(
CType(xElement, String) + vbCrLf)
Next
Dim defaultns = XNamespace.Get("http://www.xyz.com")
Dim result3 = From c In doc.Descendants(defaultns + "Child")
Select c
txtLog.AppendText("
Query for defaultns+Child\r\n")
For Each xElement In result3
txtLog.AppendText(
CType(xElement, String) + vbCrLf)
Next

txtLog.AppendText("
Done" + vbCrLf)
End Sub

--------------------------------CS-------------------------------------
private void lINQQueryNamespaceToolStripMenuItem_Click(
object sender, EventArgs e)
{
var xml =
@"
<Root xmlns:aw='http://www.adventure-works.com'
xmlns='http://www.xyz.com'>
<Child>1</Child>
<aw:Child>2</aw:Child>
<Child>3</Child>
<aw:Child>4</aw:Child>
<Child>5</Child>
<aw:Child>6</aw:Child>
</Root>
";
var doc = XDocument.Parse(xml);
txtLog.Clear();

var result1 = from c
in doc.Descendants("Child")
select c;
txtLog.AppendText("
Query for Child\r\n");
foreach (var xElement in result1)
{
txtLog.AppendText((
string)xElement + "\r\n");
}

var aw = XNamespace.Get("http://www.adventure-works.com");
var result2 = from c
in doc.Descendants(aw + "Child")
select c;

txtLog.AppendText("
Query for aw+Child\r\n");
foreach (var xElement in result2)
{
txtLog.AppendText((
string)xElement + "\r\n");
}
var defaultns = XNamespace.Get("
http://www.xyz.com");
var result3 = from c
in doc.Descendants(defaultns + "Child")
select c;

txtLog.AppendText("
Query for defaultns+Child\r\n");
foreach (var xElement in result3)
{
txtLog.AppendText((
string)xElement + "\r\n");
}
txtLog.AppendText("
Done\r\n
");
}


Resumen de la lección


·                    La clase XDocument permite en memoria, y al azar, la lectura-escritura de un XML.


·                    La clase XDocument proporciona acceso al nodo mediante el uso de las clases de LINQ to XML.


·                    Al trabajar con objetos XAttribute, puede recuperar un valor de tipo mediante el CType y puede convertir el valor del atributo con al tipo deseado.


·                    Las clases XDocument y XElement proporcionan métodos de carga del XML desde un

archivo con un XmlReader.


Las clases XDocument y XElement proporcionar un método Parse que le permite pasar de una cadena XML al correspondiente contenido XML.








No hay comentarios:

Publicar un comentario