LINQ - Usando LINQ to XML II


Vamos ser sinceros, do ponto de vista do desenvolvedor, trabalhar com XML não é uma tarefa agradável. Isso porque o modelo de objetos definidos pelo o Consórcio WWC não é fácil de usar. O framework DOM exige que você com frequência tenha que escrever um monte de código para produzir uma simples saída XML, e, se você precisa procurar um item no documento XML você vai ter que usar XPath que usa uma sintaxe de consulta nada intuitiva, que não é parecida com SQL e que você vai ter que fazer um esforço para aprender.

Aqui é deixa para o LINQ to XML entrar em cena, visto que eliminar tamanha complexidade é um dos objetivos principais do LINQ to XML. Embora a plataforma .NET lhe oferece recursos para tratar com XML , o LINQ to XML vai além, pois ele integra os operadores padrão de consulta do LINQ com XML, e, além disso, o  oferece também classes para criar e gerenciar facilmente XML.

Dessa forma o LINQ to XML permite que você fique focado no que você tem que fazer e não em como fazer tornando assim o XML apenas uma fonte de dados para o LINQ.

Realizando consultas com LINQ to XML

Desde que o LINQ to XML suporta os operadores padrão de consultas do LINQ , um documento XML pode ser carregado na memória e então ser consultado usando a sintaxe padrão do LINQ.

Abaixo temos um esquema representando a hierarquia de classes do LINQ to XML

Vamos começar com uma consulta bem simples usando o arquivo XML, Empregado.xml, descrito a seguir:

<?xml version="1.0" encoding="utf-8" ?>
<empregado>
<!--secção pessoa-->
   <pessoa>
      <id>1</id>
      <nome>Jose Carlos</nome>
      <sobrenome>Macoratti</sobrenome>
      <idperfil>1</idperfil>
   </pessoa>
   <pessoa>
      <id>2</id>
      <nome>Jefferson</nome>
      <sobrenome>Andre</sobrenome>
      <idperfil>2</idperfil>
   </pessoa>
   <pessoa>
      <id>3</id>
      <nome>Maria</nome>
     <sobrenome>Schizza</sobrenome>
     <idperfil>2</idperfil>
   </pessoa>
   <pessoa>

       <id>4</id>
       <nome>Fabio Claudio</nome>
       <sobrenome>Ferracchiati</sobrenome>
       <idperfil>1</idperfil>
   </pessoa>
   
<!--pseção perfil->
    <perfil>
           <id>1</id>                                <perfildescricao>Gerente</perfildescricao>
     </perfil>
     <perfil>
          <id>2</id>                 <perfildescricao>Analista</perfildescricao>
      </perfil>

  
  <!--seção salario->
     <salario>
      <idpessoa id="1" ano="2006" salarioano="10000,00" />
      <idpessoa id="1"ano="2007"  salarioano="15000,00" />
    </salario>
</empregado>

A classe XDocument fornece o método Elements que retorna itens a partir da classe XElement. A classe XElement representa o núcleo da library do LINQ to XML.

Um objeto XElement é a representação de um elemento existente no documento XML. Cada nó, bem como cada ramo, no documento XML é um elemento.

Para obter um valor de um elemento podemos usar o método Elements repetidamente conforme o código abaixo:

XElement xml = XElement.Load(@"..\..\Empregado.xml");

var consulta = from p in xml.Elements("empregado").Elements("pessoa")
	        where (int)p.Element("id") == 1
	        select p;

foreach(var registro in consulta)
{
	Console.WriteLine("Pessoa: {0} {1}",
	registro.Element("nome"),
	registro.Element("sobrenome"));
}

Observando a estrutura do XML você pode ver que o elemento <empregado> é a raiz e que o elemento <pessoa> aparece 4 vezes para representar 4 linhas na fonte de dados empregado.

Usando o método Elements fornecido pela classe XElement você pode retornar uma coleção de elementos e interagir através deles.


Anexando a chamada do método Elements("pessoa") ao método Elements("empregado") você pode retornar todos os 4 elementos pessoa no documento XML.

A condição where filtra o elemento pessoa para retornar aquele cujo identificador (id) é igual a 1.

O valor de um objeto XElement é sempre representado por um tipo String.

Se você quiser alterar o tipo String, você deverá efetuar uma conversão forçada (casting) para o tipo de valor desejado. No código acima estamos forçando a conversão para int para verificar o identificador na condição where.

A declaração foreach interage através dos elementos e imprime o nome de cada pessoa, neste caso usamos a propriedade Value para retornar um valor do elemento.

A classe XDocument é muito parecida com a classe XElement (contém os mesmos métodos, propriedades,...) mas ele representa o elemento raiz de um documento XML.

No exemplo acima o objeto XDocument representa o elemento empregado, e o seu método Load irá carregar o documento XML na memória

Procurando valores de atributos

Abaixo temos a informação do salário armazenada no atributo idpessoa:

<salario>
    <idpessoa id="1" ano="2006" salarioano="10000,00" />
    <idpessoa id="1" ano="2007" salarioano="15000,00" />
</salario>

Para efetuar a consulta sobre os valores do atributos podemos fazer assim:

XElement xml = XElement.Load(@"..\..\Empregado.xml");

var query = from s in xml.Elements("salario").Elements("idpessoa")
                 where (int)s.Attribute("ano") == 2006
                 select s;

foreach(var registro in consulta)
{
   Console.WriteLine("Valor: {0}", (string)registro.Attribute("salarioano"));
}

Criando Documentos XML

Criar documentos XML com LINQ to XML é muito simples, veja abaixo o código que cria um novo elemento pessoa seguido pelos seus elementos:

XElement xml = new XElement("empregado",
                       new XElement("pessoa",
                       new XElement("id", 1),
                       new XElement("nome", "Carl"),
                       new XElement("sobrenome", "Lewis"),
                       new XElement("idperfil", 2)));

Você pode criar objetos com uma hierarquia estrutural que é parecida com aquela usada pelo documento XML.

Desta forma o primeiro elemento a ser criado é o raiz empregado e ao seu construtor passamos outro objeto XElement (pessoa) que será o filho do elemento empregado: XElement xml = new XElement("people",new XElement("person"


Assim podemos passar outros objetos XElement para o construtor pessoa para definir seus elementos:

new XElement("pessoa",
new XElement("id", 1),
new XElement("nome", "Carlos"),
new XElement("sobrenome", "Lewis"),
new XElement("idperfil", 2)));

Ao final o documento XML terá a seguinte aparência exibindo o novo elemento incluído:

<empregado>
   <pessoa>
        <id>1</id>
         <nome>Carlos</nome>
         <sobrenome>Lewis</sobrenome>
         <idperfil>2</iperfil>
   </pessoa>

</empregado>


A construção Funcional é baseada no construtor da classe XElement a qual aceita um array de parâmetros de objetos:

public XElement(XName name, params object[] contents)


Como este construtor aceita um array de objetos de tipos genéricos podemos passar qualquer tipo de informação como:

- Um objeto XElement que será adicionado como elemento filho;
- Um objeto XAttribute que será usado como um atributo para o elemento atual;
- Uma valor string que será usado como valor para o elemento atual;
- Um valor null que será ignorado;
- Um objeto IEnumerable que será enumerado e seus elementos incluídos recursivamente no documento XML;
- Um valor (variável, constante, propriedade , ou chamada de método) que será usado como valor para o elemento atual;

Vejamos um exemplo de como criar um documento XML completo. Considere o seguinte documento XML:

<?xml version="1.0" encoding="UTF-16" standalone="yes"?>
<cliente id="001>
  <nome>Jose Carlos</nome>

  <sobrenome>Macoratti</sobrenome>
  <enderecos>
    <endereco type="email">macoratti@devleap.com.br</endereco>
    <endereco type="url">http://www.macoratti.net</endereco>

    <endereco type="home">Brasilia - Brasil</endereco>
  </enderecos>
</cliente>

Usando a construção funcional podemos gerar este XML usando o seguinte código:

XDocument cliente =
    new XDocument(
        new XDeclaration("1.0", "UTF-16", "yes"),
        new XElement("cliente",
            new XAttribute("id", "001"),
            new XElement("nome", "Jose Carlos"),
            new XElement("sobrenome", "Macoratti"),
            new XElement("enderecos",
                new XElement("endereco",
                    new XAttribute("type", "email"),
                        "macoratti@devleap.com.br"),
                    new XElement("endereco",
                    new XAttribute("type", "url"),
                        "http://www.macoratti.net"),
                    new XElement("endereco",
                    new XAttribute("type", "home"),
                        "Brasilia - Brasil"))));

Outra forma para criar um arquivo XML é usar a classe XElement. Vamos um arquivo XML genérico chamado Times.xml e definir  seus os atributos usando os métodos SetAttributeValue e SetELementValue via código; para salvar o arquivo vamos usar o método Save:

        
        Dim arquivo As String = "C:\dados\Times.xml"
        Dim documentoXml As New XElement("Times")
        Dim element As XElement = Nothing
        For I As Integer = 1 To 3
            element = New XElement("Time", New XComment("Atributos do time " + I.ToString()))
            'Atributos   
            element.SetAttributeValue("Nome", String.Format("Nome", I))
            element.SetAttributeValue("Cidade", String.Format("Cidade", I))
            'Elementos   
            element.SetElementValue("Vitorias", String.Format("Vitorias", I))
            element.SetElementValue("Empates", String.Format("Empates", I))
            element.SetElementValue("Derrotas", String.Format("Derrotas", I))
            'Adiciona elemento ao elemento raiz   
            documentoXml.Add(element)
        Next
        documentoXml.Save(arquivo)

 

Note que usamos o método XComment para incluir comentários no arquivo XML gerado.

Este código irá gerar o arquivo Times.xml na pasta c:\dados. Abrindo o arquivo gerado temos:

Nota: Para carregar um arquivo XML podemos usar o método Load.

Abaixo um exemplo de código que pode ser usado para criar um documento XML compatível com RSS 2.0:

XDocument doc = new XDocument(
    new XDeclaration("1.0", "utf-8", "yes"),
    new XComment("Exemplo de gerador RSS"),
    new XElement("rss",
        new XAttribute("version", "2.0"),
        new XElement ("channel",
            new XElement("title", "RSS Channel Title"),
            new XElement("description", "RSS Channel Description."),
            new XElement("link", "http://www.macoratti.net"),
            new XElement("item",
                new XElement("title", "Titulo primeiro artigo"),
                new XElement("description", "descricao primeiro artigo"),
                new XElement("pubDate", DateTime.Now.ToUniversalTime()),
                new XElement("guid", Guid.NewGuid())),
            new XElement("item",
                new XElement("title", "Titulo segundo artigo"),
                new XElement("description", "descricao segundo artigo"),
                new XElement("pubDate", DateTime.Now.ToUniversalTime()),
                new XElement("guid", Guid.NewGuid()))
            )
        )
     );

doc.Save(@"
c:\exemplo.xml");
 

Alterando e excluindo valores em arquivos XML

Com LINQ to XML alterar e excluir valores no XML é bem intuitivo.

a - Alterando valores de Elementos

Abaixo estamos alterando o elemento Nome do arquivo Empregado.xml:

    Dim arquivo As String = "C:\dados\Empregado.xml"           Dim xml As XElement = XElement.Load(arquivo)

    Dim elements As IEnumerable(Of XElement) = xml.Elements()   

     'Seleciona e altera o elemento Nome onde o valor seja igual a "Jose Carlos"      For Each item In elements.Elements("Nome").Where(Function(e) e.Value = "Jose Carlos")           item.Value = "Antonio Carlos"  
    Next  

    'Salva Alterações   
    xml.Save(arquivo)   

b - Alterando valores de Atributos

Abaixo estamos alterando o atributo Cidade do arquivo Times.xml:

    Dim arquivo As String = "C:\dados\Times.xml"           Dim xml As XElement = XElement.Load(arquivo)

    Dim elements As IEnumerable(Of XElement) = xml.Elements()   

     'Seleciona e altera o elemento Cidade onde o valor seja igual a "Cidade 1"         For Each item In elements.Attributes("Cidade").Where(Function(e) e.Value = "Cidade 1")           item.Value = "Brasilia"  
    Next  

    'Salva Alterações   
    xml.Save(arquivo)   

b - Excluindo um elemento

A seguir estamos excluindo o elemento Time do arquivo Times.xml para uma condição:

    Dim xml As XElement = XElement.Load(arquivo)

    Dim elements As IEnumerable(Of XElement) = xml.Elements()     

    'Exclui elemento Time aonde o Atributo Nome seja igual a "Nome 1"      elements.AncestorsAndSelf("Time").Where(Function(e) e.Attribute("Time").Value = "Nome 1").Remove()   
   'Salva Alterações   
   xml.Save(arquivo)   

O método AncestorsAndSelf  Retorna uma coleção de elementos que contém cada elemento na coleção de origem, e os predecessores do cada elemento da coleção de origem.

Você pode chamar esse método como um método de instância em qualquer objeto do tipo IEnumerable<(Of <(XElement>)>). Quando você usa a sintaxe método de instância para chamar esse método, omita o primeiro parâmetro.

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

Referencias:

   - Usando LINQ To XML
   - LINQ to XML 


José Carlos Macoratti