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" 'Salva Alterações
|
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" 'Salva Alterações
|
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()
|
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:
José Carlos Macoratti