.NET - Consultado XML com LINQ


A LINQ - Language integrated Query - é um conjunto de recursos introduzidos no .NET Framework 3.5 que permitem a realização de consultas diretamente em base de dados , documentos XML , estrutura de dados , coleção de objetos ,etc. usando uma sintaxe parecida com a linguagem SQL.

Não importa a origem dos dados pois com LINQ podemos usar praticamente expressões semelhantes para realizar consultas tornando assim trabalho do desenvolvedor mais produtivo.

Neste artigo eu vou mostrar como podemos usar LINQ para consultar uma fonte de dados XML obtendo informações sobre elementos.

Nos exemplos usados neste artigo eu vou tomar como base o arquivo XML denominado Estoque.xml que possui a seguinte estrutura:(Estou mostrando apenas parte do arquivo para não ocupar espaço)

 <Produtos>
    <ProdutoID>355</ProdutoID>
    <CategoriaID>16</CategoriaID>
    <ModeloNumero>RU007</ModeloNumero>
    <ModeloNome>Rain Racer 2000</ModeloNome>
    <ProdutoImagem>image.gif</ProdutoImagem>
    <CustoUnitario>1499.99</CustoUnitario>
  </Produtos>
  <Produtos>
    <ProdutoID>356</ProdutoID>
    <CategoriaID>20</CategoriaID>
    <ModeloNumero>STKY1</ModeloNumero>
    <ModeloNome>Edible Tape</ModeloNome>
    <ProdutoImagem>image.gif</ProdutoImagem>
    <CustoUnitario>3.99</CustoUnitario>
  </Produtos>
   ..................
  <Categorias>
    <CategoriaID>14</CategoriaID>
    <CategoriaNome>Communications</CategoriaNome>
  </Categorias>
  <Categorias>
    <CategoriaID>15</CategoriaID>
    <CategoriaNome>Deception</CategoriaNome>
  </Categorias>
   ......................
  <Categorias>
    <CategoriaID>20</CategoriaID>
    <CategoriaNome>General</CategoriaNome>
  </Categorias>
</NewDataSet>

Consultando XML com LINQ

A classe System.Xml.Linq.XElement é uma fonte de dados válido para consultas LINQ. A seqüência básica para consultar uma árvore XML pode ser resumida asim:

1. Inicie uma nova consulta utilizando LINQ usando palavra-chave from, fornecendo um nome de variável que você vai usar para fazer seleções (por exemplo, do elemento em root.Elements ()).
2. Identifique as condições para o uso em elementos de selecção com a qual palavra-chave
Where;
3. Indique qual o valor será adicionado ao conjunto de resultados de cada elemento correspondente usando a palavra chave de seleção
Select;
4. Especifique o caminho em que deseja que os resultados sejam classificados usando a palavra-chave
orderby;

Ao utilizar XElement como fonte o LINQ, o resultado será um IEnumerable de XElements, contendo os elementos de sua árvore XML que correspondem à sua pesquisa.

As LINQ consultas podem ser escritas usando palavras-chaves que foram adicionadas ao C # (from, where, select, etc), ou usando métodos de instância que preencham a mesma finalidade; no caso de XElement, você deve chamar o método de instância Elements para obter um IEnumerable <XElement> para usar como base para suas consultas.

Para selecionar um elemento, você pode usar as propriedades e métodos da classe XElement. Por exemplo, para encontrar todos os elementos em uma árvore XML que possuem um atributo de cor com valor igual azul, você usuaria:   

from element in root.Elements () where (string)element.Attribute ("cor") == "blue" select element;

Para alcançar os mesmos resultados usando métodos de instância faríamos assim:

root.Elements().Where(e => (string)e.Attribute("cor") == "blue").Select(e => e);

O tipo de resultado de uma consulta LINQ depende de que tipo de elemento que você recupera com a palavra-chave Select, mas sempre será retornada uma instância de IEnumerable, que você pode usar em um loop foreach para executar através dos elementos combinados, ou como base para mais consultas LINQ.

O exemplo anterior retorna um <XElement> IEnumerable. Se você usar a palavra-chave Select para obter um valor que representa uma característica de um elemento, como um valor de atributo, então o tipo genérico de IEnumerable será diferente.

No exemplo a seguir vamos carregar uma árvore XML do arquivo Estoque.xml .

A seguir vamos procurar e selecionar os Elements que são denominados produtos e que têm um valor de 16 para o elemento filho CategoriaID, imprimindo em seguida o valor do elemento filho ModeloNome;

A consulta usada é feita uma vez utilizando as palavras-chave LINQ e feita novamente usando os
métodos de instância e expressões lambda. Qual você vai adotar é uma questão de preferência.

Abra o Visual C# 2010 Express Edition e crie um novo projeto Windows Forms Application com o nome ConsultaXML_Linq;

A seguir inclua os seguintes controles no formulario form1.cs:

Conforme o leiaute da figura abaixo:

Agora vamos declarar os namespaces usados no formulário:

using System;
using System.IO;
using System.Xml;
using System.Xml.Linq;
using System.Windows.Forms;

A seguir vamos declarar no início do formulário a variável para receber o nome e caminho do arquivo XML:

string nomeArquivoXML;
XElement root;

No evento Click do botão que irá abrir a caixa diálogo para selecionar o arquivo insira o código abaixo:

   private void btnProcurar_Click(object sender, EventArgs e)
    {
            OpenFileDialog dialogo = new OpenFileDialog();
            dialogo.Filter ="txt files (*.xml)|*.xml|All files (*.*)|*.*";
            dialogo.InitialDirectory = "C:\\dados";
            dialogo.Title = "Selecione um arquivo XML";
            if (dialogo.ShowDialog() == DialogResult.OK)
            {
                txtArquivoXML.Text = dialogo.FileName;
            }
            else
            {
                txtArquivoXML.Text = "";
            }
    }

A seguir em cada evento Click de cada botão de comando vamos incluir o código pertinente:

- Botão : Carregar XML - Carrega e exibe o conteúdo do XML no TextBox:

  private void btnCarregaXML_Click(object sender, EventArgs e)
        {
            if (txtArquivoXML.Text == string.Empty)
            {
                MessageBox.Show("Informe o nome do arquivo XML a carregar.", "XML", MessageBoxButtons.OK, MessageBoxIcon.Information);
            }
            else
            {
                nomeArquivoXML = @txtArquivoXML.Text;
                try
                {
                    root = XElement.Load(nomeArquivoXML);
                    txtXML.Text = root.ToString();
                    //carrega o XML no TextBox
                    txtXML.Text = root.ToString();
                }
                catch (Exception ex)
                {
                    MessageBox.Show("Erro : " + ex.Message);
                }
            }
        }

- Botão : Consulta LINQ - Realiza a consulta LINQ no XML usando o critério definido e exibe o resultado no ListBox;

 private void btnConsultaLINQ_Click(object sender, EventArgs e)
        {
            //consulta linq
            IEnumerable<string> catEnum = from elem in root.Elements()
                                          where (elem.Name == "Produtos" && ((string)elem.Element("CategoriaID")) == "16")
                                          select ((string)elem.Element("ModeloNome"));

            //exibe o resultado no ListBox
            lsResultado.Items.Clear();
            foreach (string stringVal in catEnum)
            {
                lsResultado.Items.Add("Categoria 16 item: " + stringVal);
            }
        }

Neste código estamos realizando uma consulta para obter os produtos cuja código da categoria é igual a 16 e exibimos o nome do modelo;

- Botão : Carregar XML - Realiza a consulta LINQ usando métodos de extensão no XML usando o critério definido e exibe o resultado no ListBox;

  private void btnMetodosInstancia_Click(object sender, EventArgs e)
        {
            //usando métodos de instância
            IEnumerable<string> catEnum = root.Elements().Where( el => el.Name == "Produtos" && (string)el.Element("CategoriaID") == "20").Select(el => (string)el.Element("ModeloNome"));
            
            //exibe o resultado no ListBox
            lsResultado.Items.Clear();
            foreach (string stringVal in catEnum)
            {
                lsResultado.Items.Add("Categoria 20 item: " + stringVal);
            }
        }

Neste código estamos realizando uma consulta para obter os produtos cuja código da categoria é igual a 20 e exibimos o nome do modelo;

Executando o projeto iremos obter:

Vejamos a seguir outras consultas a título de exemplo:

1- Consultar os produtos e exibir o seu nome e custo unitário usando a propriedade Descendants;

      private void btnQueryLINQ_Click(object sender, EventArgs e)
        {
            XDocument xmlDoc = XDocument.Load(nomeArquivoXML);

            var q = from c in xmlDoc.Descendants("Produtos")
                        select (string)c.Element("ModeloNome") + " -- " + (string)c.Element("CustoUnitario");
            
            foreach (string item in q)
            {
                lsResultado.Items.Add("Produtos : " + item);
            }
        }

No código acima estamos usando a propriedade Descendants que acessa os elementos descentes de um elemento particular.

2- Consultar os produtos e exibir o nome e número do modelo para uma categoria particular usando a cláusula Where;

      private void btnQueryLINQ1_Click(object sender, EventArgs e)
        {
            XDocument xmlDoc = XDocument.Load(nomeArquivoXML);
            
            var q = from c in xmlDoc.Descendants("Produtos")
                       where c.Element("CategoriaID").Value == "16"
                       select (string)c.Element("ModeloNome") + " ==>" + (string)c.Element("ModeloNumero");
            
            foreach (string item in q)
            {
                lsResultado.Items.Add("Produtos : " + item);
            }
        }

3- No código abaixo definimos um tipo anônimo em tempo de execução com duas propriedades : id e nome que são criadas com a palavra-chave new usada na cláusula Select e as propriedades são definidas no interior do corpo da instrução new { }.

  • Tipos anônimos fornecem uma maneira de encapsular um conjunto de propriedades em um objeto sem ter que primeiro definir um tipo explicitamente.
  • O maneira mais comum de se inicializar um tipo anônimo é com algumas propriedades de um outro objeto.
  • Os tipos das propriedades de um tipo anônimo são gerados pelo compilador através de inferência durante a inicialização do objeto.
  • Tipos anônimos são usados com muita frequência quando utilizamos LINQ.

A seguir acessamos o novo tipo no laço foreach para exibir os valores dos códigos dos produtos e respectivo nome de modelo para produtos com categoria igual a 20;

   private void btnQueryLINQ2_Click(object sender, EventArgs e)
        {
            XDocument xmlDoc = XDocument.Load(nomeArquivoXML);
           
            var q = from c in xmlDoc.Descendants("Produtos")
                    where c.Element("CategoriaID").Value == "20"
                    select new
                    {
                        id = c.Element("ProdutoID").Value,
                        nome = c.Element("ModeloNome").Value
                    };
            
            foreach (var obj in q)
            {
                lsResultado.Items.Add("Produtos : " +  obj.id + " -- " + obj.nome);
            }
        }

E dessa forma você viu como o LINQ pode facilitar a sua vida para realizar consultas em fonte de dados XML.

Simples , simples assim...

Pegue o projeto completo aqui: ConsultaXML_Linq.zip

Eu sei é apenas LINQ , mas eu gosto...

Referências:

José Carlos Macoratti