XmlDocument e XmlTextReader

Continuando com as séries de postagens sobre XML, agora vamos colocar a mão na massa e descobrir quais são as classes que o .NET nos oferece para se trabalhar com XML. Tanto faz se você pretende trabalhar com escrita, leitura ou até mesmo empregar o uso de XPath. Para quem não acompanhou desde o começo esta série, recomendo que veja as postagens sobre o assunto antes de prosseguir, se você já conhece bem sobre o assunto sinta-se a vontade para continuar.

Apenas como referência, utilizaremos aquele XML da postagem anterior:

<?xml version="1.0" encoding="utf-8"?>
<Pedidos>
  <!--Este Elemento representara a estrutura de um 'Pedido' realizado pelo cliente  -->
  <Pedido IdPedido="1">
    <ItensDoPedido>
      <Item IdItem="557">
        <Nome>Geladeira XAVECO</Nome>
        <Preco>1000</Preco>
      </Item>
      <Item IdItem="558">
        <Nome>Cadeiro XAMEGO</Nome>
        <Preco>80.5</Preco>
      </Item>
    </ItensDoPedido>
  </Pedido>
  <!--Este Elemento representara a estrutura de um 'Pedido' realizado pelo cliente  -->
  <Pedido IdPedido="2">
    <ItensDoPedido>
      <Item IdItem="559">
        <Nome>Celular CADEMEUCHIPPEDROOO</Nome>
        <Preco>500</Preco>
      </Item>
    </ItensDoPedido>
  </Pedido>
</Pedidos>

XmlDocument

Disponível desde a versão 2.0 do .NET Framework, a classe XmlDocument provê fácil manipulação e criação de XMLs, sendo necessário que em seu projeto esteja referenciado o assembly System.Xml.dll e a importação do namespace System.Xml. Com a classe XmlDocument podemos trabalhar com a representação de um XML em memória, usando de especificações DOM Level 1 e 2, possibilitando a fácil navegação entres os nós deste documento, ou até mesmo agindo como um facilitador para operações corriqueiras, tais como a seleção de um único nó com base no seu ID, substituição de nós existentes, clonagem de nós, normalização e muito mais. Veja abaixo os principais métodos disponíveis para se trabalhar com a classe XmlDocument:

Método Descrição
Load Carrega um documento XML de um Stream, disco rígido ou URL.
LoadXml Carrega um documento XML a partir de um string.
GetElementById Localiza e retorna um único elemento cujo seu atributo ID, indiferente da tag que este elemento possua.
GetElementByTagName Localiza e retorna uma lista de XmlNode contendo todos os elementos baseado na pesquisa pela tag.
SelectNodes Seleciona uma lista de nós que seja compartíveis com o XPath empregado.
SelectSingleNode Seleciona o primeiro elemento na lista de nós que seja compartível com o XPath empregado.

Como eu já disse, o uso destes métodos é bem simples e intuitivo, porém atente-se ao 3º exemplo. Vejam:

//Declarando uma nova instancia XmlDocument
XmlDocument xmlDocument = new XmlDocument();

//1º exemplo --------------------------------------------------------------

//Carregando XML do disco rigido
xmlDocument.Load(@"C:\ArquivoSuperPesado.xml");

Console.WriteLine("Recuperando todos os elementos Pedido e exibindo o seu respectivo id");

foreach (XmlNode xmlNode in xmlDocument.GetElementsByTagName("Pedido"))
    Console.WriteLine("Id do Pedido: {0}", xmlNode.Attributes["IdPedido"].Value);

Console.WriteLine();

//2º exemplo --------------------------------------------------------------

Console.WriteLine("Recuperando os elementos cuja tagname é igual a Pedido ou Preço");

foreach (XmlNode xmlNode in xmlDocument.SelectNodes("//Preco | //Pedido"))
{
    Console.Write("Tipo: {0}\t Nome: {1}\t Atributo: ", xmlNode.NodeType, xmlNode.Name);

    foreach (XmlAttribute attr in xmlNode.Attributes)
        Console.Write("{0}={1} ", attr.Name, attr.Value);

    Console.WriteLine();
}

Console.WriteLine();

//3º exemplo --------------------------------------------------------------

Console.WriteLine("Somando o preço de todos os itens do pedido cujo o Id é 1");

string xpathWithFunction = "sum(//Pedido[@IdPedido='1']/ItensDoPedido/Item/Preco)";

//Usando de uma função XPath
object result = xmlDocument.CreateNavigator().Evaluate(xpathWithFunction);

Console.WriteLine(Convert.ToDecimal(result));

Notaram que no 3º exemplo usamos uma função XPath, com o intuito de somar o preço de todos os itens, porém esta função não foi executada com o método SelectNode ou até mesmo com o método SelectSingleNode, caso você tente usar uma função XPath sendo que o retorno desta função seja algo diferente de um nó xml ou um conjunto de nós xmls, uma exceção do tipo XPathException será disparada, informado que a expressão XPath deve ser avaliada para um conjunto de nós, logo a função XPath sum() causaria esta exceção, pois o seu retorno é numérico e não um nó xml, atente-se ao que será executado, pois cada função XPath possuí retorno diferente, caso o método utilizado não suporte este retorno, exceções poderão ser disparadas.

O método CreateNavigator() nos fornece uma instancia da classe XPathNavigator, uma classe própria para se trabalhar com XPath, sendo que quando a instância desta classe é fornecida por uma instancia da classe XmlDocument o atual objeto nos permite edições do xml, algo não permitido caso eu venha carregar meu documento xml diretamente na classe XPathDocument. Por final o método Evaluate() avalia e  executa o XPath informado no parâmetro, provendo como retorno do tipo object, sendo necessário fazer a conversão para o tipo que lhe convém.

Vejamos abaixo o resultado da execução deste snippet de código:

xmldocument-result

A classe XmlDocument também nos permite criar xml de maneira simples e rápida, provendo de métodos que nos fornecem instâncias de outras classes necessárias para se criar um xml, tais como as classes XmlDeclaration, XmlElement e XmlAttribute. Vejamos alguns deles:

Método Descrição
CreateElement Fornece uma instância da classe XmlElement.
CreateAttribute Fornece uma instância da classe XmlAttribute, que pode ser anexada junto da instância XmlElement através da propriedade Attributes.
AppendChlid Método cuja função é inserir elementos xml dentro da instância da classe XmlDocument. Também é utilizado para inserir elementos xml dentro de instância da classe XmlElement.
CreateDeclaration Fornece uma instância da classe XmlDeclaration, responsável por armazenar versão do xml, encoding e se o mesmo é standalone.
Save Método responsável por salvar o documento xml, podendo informar como saída um caminho

Bom, que tal eu mostrar um exemplo na prática cuja função é montar um xml contendo alguns blogs que acompanho, vejam:

//Blogs
Dictionary<string, string> blogs = new Dictionary<string, string>();

blogs.Add("Fernando Henrique Ferreira", "http://ferhenriquef.com/");
blogs.Add("Eduardo Pires", "http://www.eduardopires.net.br/");
blogs.Add("Eric Milaneze", "http://www.milaneze.com.br/");

//Declarando uma nova instancia XmlDocument
XmlDocument xmlDocument = new XmlDocument();

//Criando declaração
xmlDocument.AppendChild(xmlDocument.CreateXmlDeclaration("1.0", "utf-8", null));

//Criando elemento principal
XmlElement blogsElement = xmlDocument.CreateElement("blogs");

//Adicionando ao documento xml
xmlDocument.AppendChild(blogsElement);

foreach (KeyValuePair<string, string> blog in blogs)
{
    //Criando elemento blog
    XmlElement blogElement = xmlDocument.CreateElement("blog");

    //Criando elemento autor e atribuindo autor
    XmlElement autorElement = xmlDocument.CreateElement("autor");
    autorElement.InnerText = blog.Key;

    //Criando atributo url e atribuindo url do blog
    XmlAttribute atrr = xmlDocument.CreateAttribute("url");
    atrr.Value = blog.Value;

    //Adicionando atributo ao nó blog
    blogElement.Attributes.Append(atrr);

    //Adicionando o autor ao blog
    blogElement.AppendChild(autorElement);

    //Adicionando o blog a coleção de blogs
    blogsElement.AppendChild(blogElement);
}

//Salvando xml
xmlDocument.Save(@"C:\Blogs.xml");

O resultado final é este:

<?xml version="1.0" encoding="utf-8"?>
<blogs>
  <blog url="http://ferhenriquef.com/">
    <autor>Fernando Henrique Ferreira</autor>
  </blog>
  <blog url="http://www.eduardopires.net.br/">
    <autor>Eduardo Pires</autor>
  </blog>
  <blog url="http://www.milaneze.com.br/">
    <autor>eric Milaneze</autor>
  </blog>
</blogs>

XmlTextReader

xmlreaderA classe XmlReader é uma classe abstrata, que fornece métodos para leitura e análise de um arquivo xml. Uma das principais classes que usam desta abstração do XmlReader é a implementação XmlTextReader, que tem por função ler nó por nó do arquivo xml, fornecendo uma das mais rápidas maneiras e com menor consumo de memória para ler e analisar dados xml, sendo somente uma leitura e apenas de ida, sem armazenar o arquivo por inteiro em memória.

Caso exista a necessidade de manipularmos o arquivo xml, esta classe não é recomendada, pois sua função é justamente a leitura e não a manipulação do arquivo contido na instância.

Abaixo um exemplo de leitura utilizando a classe XmlTextReader:


using (XmlReader xmlReader = new XmlTextReader(@"C:\XmlDocumentTest.xml"))
{
    while (xmlReader.Read())
    {
        switch (xmlReader.NodeType)
        {
            case XmlNodeType.XmlDeclaration:
                Console.WriteLine("Declaração: {0} - {1}", xmlReader.Name, xmlReader.Value);
                break;
            case XmlNodeType.Element:
            case XmlNodeType.Comment:
                Console.WriteLine("Elemento: {0} - {1}", xmlReader.Name, xmlReader.Value);
                break;
            case XmlNodeType.Text:
                Console.WriteLine("Texto: {0}", xmlReader.Value);
                break;
        }

        if (xmlReader.HasAttributes)
        {
            while (xmlReader.MoveToNextAttribute())
            {
                Console.WriteLine("Atributo: {0} - {1}", xmlReader.Name, xmlReader.Value);
            }
        }
    }
}

A utilização da classe XmlDocument e XmlTextReader não se limita apenas ao que foi mostrado acima, existem infinitas possibilidades e combinações que podemos fazer para se extrair o máximo de um xml de forma rápida e simples. Espero que tenham gostado pessoal e até a próxima.