 ASP
.NET - Manutenção de dados em arquivos XML
ASP
.NET - Manutenção de dados em arquivos XML
Olhando para o passado, 5 ou 7 anos
atrás, podemos ver como as ferramentas para desenvolvimento de
software evoluíram. Basta comparar o tempo que era gasto para
realizar uma tarefa simples como tratar arquivos XML com o tempo
que se gasta hoje para se ter uma idéia; citei o exemplo dos
arquivos XML porque eles serão o foco deste artigo.
Particularmente a plataforma .NET evoluiu muito e atualmente apresenta uma nova suíte de APIs para XML construídas com base nos padrões DOM, XPath, XSD e XSLT. As classes XML da plataforma .NET oferecem um desempenho muito bom e possuem um modelo de programação bem familiar que estão vinculadas às APIs de acesso a dados ADO .NET.
As classes XmlWriter, XmlReader e XmlNavigator e as classes que derivam dela, incluindo XMLTextReader e XMLTextWriter encapsulam um número de funcionalidades que permitem um grande aumento de desempenho e produtividade substituindo tarefas que antes tinham que ser construídas manualmente e com muito trabalho.
Neste artigo eu vou mostrar como gerenciar arquivos XML realizando operações de seleção, inclusão, alteração e exclusão de informações em arquivos XML usando as novas funcionalidades da plataforma .NET com a linguagem C#.
Para acompanhar o exemplo deste artigo você pode usar o Visual Web Developer 2008 Express Edition ou o Visual Studio 2008.
Definição de requisitos, leiaute e configuração
Vou iniciar apresentando o leiaute do formulário web principal do nosso web site representado pela página Default.aspx:
|  | 
| Figura 1.0 - leiaute da página Default.aspx | 
Temos aqui um formulário web simples contendo os seguintes controles:
O GridView apresenta as seguintes definições para os campos: Autor, Titulo e Conteudo;
|  | 
| Figura 2.0 - Definições para os campos gerenciados | 
O objetivo é gerenciar informações sobre poesias como autor, título e conteúdo permitindo a seleção, inclusão, alteração e exclusão de informações.
Nota: No exemplo eu estou tratando poemas de autoria do famoso poeta Fernando Pessoa.
Efetuar tal tarefa usando um banco de dados com os recursos do ASP .NET é uma tarefa simples e você têm a sua disposição uma suíte de ferramentas e opções como ADO .NET, SQLDataSource, ObjectDataSource, LINQ , etc. que lhe permitem tratar o caso de diversas formas.
A novidade é que vamos gerenciar estas informações a partir de um arquivo xml chamado poemas.xml que esta na pasta App_Data e que contém a seguinte estrutura:
| <?xml version="1.0" encoding="utf-8"?>
<poetas>
  <poemas>
    <autor></autor>
    <titulo></titulo>
    <conteudo></conteudo>
  </poemas>
  <poemas>
</poetas>			 | 
Vamos então mostrar como usar os recursos da plataforma .NET para efetuar a manutenção das informações do arquivo poemas.xml realizando operações de seleção, inclusão, alteração e exclusão de dados.
Vamos iniciar abrindo o VWD 2008 Express e criando um novo web site a partir do menu File->New web site;
Em Templates selecione ASP .NET Web Site, escolha a linguagem C# e informe o nome manuXML para o web site clicando em OK;
|  | 
Agora vamos criar a pasta App_Data em nosso projeto clicando com o botão direito do mouse sobre o nome do web site e selecionando a Add ASP .NET Folder => App_Data;
Em seguida vamos criar o arquivo xml poemas.xml na pasta App_Data clicando com o botão direito sobre a pasta e selecionando Add New Item;
A seguir informe o nome poemas.xml e clique em Add;
|  | 
Defina a estrutura do arquivo conforme já mostramos
| <?xml version="1.0" encoding="utf-8"?>
<poetas>
  <poemas>
    <autor></autor>
    <titulo></titulo>
    <conteudo></conteudo>
  </poemas>
  <poemas>
</poetas> | 
Agora vamos criar o leiaute do formulário web conforme o leiaute e definições da figura 1.0 (Figura 1.0 - leiaute da página Default.aspx) e figura 2.0 mostradas anteriormente.
Já temos tudo pronto: o leiaute e as definições da página Default.aspx e o arquivo poemas.xml.
Gerenciando informações em arquivos XML
Vamos iniciar definindo o código do evento Load da página Default.aspx que deverá carregar os dados do arquivo poemas.xml e preencher o GridView conforme a figura abaixo:
|  | 
Nota: Especifique a diretiva using no espaço para nome System.Xml para que não seja necessário qualificar declarações código: using System.Xml;
Carregando dados
Temos abaixo o código do evento Load:
|   protected void Page_Load(object sender, EventArgs e)
    {
        if (!Page.IsPostBack)
        {
            carregaDadosXML();
            this.Session["Incluir"] = 0;        }
    } | 
Eu defini uma variável de sessão Incluir e atribui um valor inicial igual a 0. Essa variável irá controlar a inclusão de dados. Veremos abaixo o porque desta definição.
A rotina carregaDadosXML() é quem vai acessar o arquivo poemas.xml e carregar o GridView, vejamos o seu código abaixo:
|     /// <summary>
    /// carrega os dados xml
    /// </summary>
    private void carregaDadosXML()
    {
        DataSet ds = new DataSet();
        ds.ReadXml(Server.MapPath(@"App_Data\poemas.xml"));        if (ds.Tables.Count > 0)
        {
            this.GridView1.DataSource = ds;
            this.GridView1.DataBind();
        }
    } | 
Como pretendemos gerenciar os dados do arquivo xml devemos criar uma estrutura em memória com as informações do arquivo poemas.xml. Fazemos isso criando um objeto DataSet e em seguida usando o método ReadXML() que para ler o conteúdo do arquivo na pasta App_Data.
Nota: O método ReadXML possui diversas sobrecargas. Veja detalhes em: http://msdn.microsoft.com/pt-br/library/system.data.dataset.readxml.aspx
|  Poderíamos 
    criar uma instância de um objeto XmlTextReader e preenchê-lo com o 
    arquivo XML. Normalmente, a classe XmlTextReader é usada quando é 
    necessário acessar o XML como dados brutos, sem a sobrecarga de um DOM. 
    Dessa maneira, a classe XmlTextReader fornece um mecanismo mais 
    rápido para leitura de XML. A classe XmlTextReader possui construtores diferentes para especificar a localização dos dados XML. O código a seguir cria uma instância da classe XmlTextReader e carrega o arquivo poemas.xml. 
 Após criar o objeto XmlTextReader, use o método Read para ler os dados XML. O método Read continua se movendo pelo arquivo XML seqüencialmente até atingir o final do arquivo, onde o método Read retornará um valor "False." 
 | 
Em seguida verificamos e o dataset foi preenchido e exibimos o resultado no GridView.
Limpando o formulário
Vamos mostrar agora o código associado ao evento Click do botão Limpar:
|   protected void btnLimpar_Click(object sender, EventArgs e)
    {
        this.TextBox1.Text = "";
        this.TextBox2.Text = "";
        this.TextBox3.Text = "";
        this.TextBox1.Text = "NOVO";
        this.TextBox1.Focus();
        Session["Incluir"] = 1;
    } | 
Este código apenas limpa o conteúdo das caixas de texto do formulário web. Lembre-se que eu defini uma variável de sessão chamada Incluir no evento Load e atribui um valor inicial zero. Ao limpar o conteúdo das caixas de texto eu estou alterando o seu valor para 1. Dessa forma eu posso controlar a inclusão da seguinte forma. O usuário somente poderá incluir valores após limpar as caixas de texto ou seja quando o valor da variável de sessão Incluir for igual a 1.
Se o usuário tentar incluir uma informação irá receber uma mensagem solicitando que os campos sejam limpos.
Incluindo dados
Vejamos então o código do evento Click do botão Incluir:
| protected void btnIncluir_Click(object sender, EventArgs e)
    {
        if ((int)Session["Incluir"] == 1)
        {
            if (TextBox1.Text.Equals("") || TextBox2.Text.Equals("") || TextBox3.Text.Equals(""))
            {
                this.RegisterClientScriptBlock("alertmessage", "<script>alert('Preencha os campos do formulário.')</script>");
            }
            else
            {
                //define um documento XML e carrega o seu conteúdo 
                XmlDocument xmldoc = new XmlDocument();
                xmldoc.Load(Server.MapPath(@"App_Data\poemas.xml"));
                //Cria um novo elemento poemas  e define os elementos autor, titulo e conteudo
                XmlElement novoelemento = xmldoc.CreateElement("poemas");
                XmlElement xmlAutor = xmldoc.CreateElement("autor");
                XmlElement xmlTitulo = xmldoc.CreateElement("titulo");
                XmlElement xmlConteudo = xmldoc.CreateElement("conteudo");                //atribui o conteúdo das caixa de texto aos elementos xml
                xmlAutor.InnerText = this.TextBox1.Text.Trim();
                xmlTitulo.InnerText = this.TextBox2.Text.Trim();
                xmlConteudo.InnerText = this.TextBox3.Text.Trim();                //inclui os novos elementos no elemento poemas
                novoelemento.AppendChild(xmlAutor);
                novoelemento.AppendChild(xmlTitulo);
                novoelemento.AppendChild(xmlConteudo);                //inclui o novo elemento no XML
                xmldoc.DocumentElement.AppendChild(novoelemento);	//Salva a inclusão no arquivo XML
                xmldoc.Save(Server.MapPath(@"App_Data\poemas.xml"));
                this.Session["Incluir"] = 0;
                //exibe os dados no GridView
                carregaDadosXML();
            }
        }
        else
        {
            this.RegisterClientScriptBlock("alertmessage", "<script>alert('Limpe os campos do formulário para incluir um novo item.')</script>");
        }
    } | 
Primeiro verificamos se a nossa variável de sessão Incluir tem o valor 1. Se o valor for diferente exibiremos a mensagem : 'Limpe os campos do formulário para incluir um novo item'.
this.RegisterClientScriptBlock("alertmessage", "<script>alert('Limpe os campos do formulário para incluir um novo item.')</script>");
Estamos fazendo isso usando o método RegisterClientScriptBlock que permite que você coloque uma função JavaScript no topo da página.
| 
     Para detalhes veja o meu artigo: ASP.NET - Messagebox | 
Em seguida usamos o mesmo recurso para exibir uma mensagem ao usuário caso as caixas de textos não forem preenchidas.
this.RegisterClientScriptBlock("alertmessage", "<script>alert('Preencha os campos do formulário.')</script>");
Satisfeitas estes critérios começamos o processo de inclusão de uma nova informação no arquivo poemas.xml.
Estamos criando uma instância da classe XmlDocument que representa o documento XML e contém um método Load para carregar o documento a partir de um arquivo, fluxo ou XmlReader.
                
XmlDocument xmldoc = new XmlDocument();
                
xmldoc.Load(Server.MapPath(@"App_Data\poemas.xml"));
A seguir criamos novos elementos usando o 
método CreateElement da classe XmlDocument.
                
//Cria um novo nó poemas e define os elementos autor, titulo e conteudo para 
este nó
                
XmlElement novoelemento = xmldoc.CreateElement("poemas");
                
XmlElement xmlAutor = xmldoc.CreateElement("autor");
                
XmlElement xmlTitulo = xmldoc.CreateElement("titulo");
                
XmlElement xmlConteudo = xmldoc.CreateElement("conteudo")
Embora esse método cria o novo objeto no contexto do documento, ele automaticamente não adicionar o novo objeto à árvore de documento. Para adicionar o novo objeto, você deve explicitamente chamar um dos métodos inserir do nó.
A seguir estamos atribuindo os valores informados na caixa de texto aos elementos criados usando a propriedade InnerText que obtêm ou define valores concatenados do nó e todos os seus nós filhos.
                
//atribui o conteúdo das caixa de texto aos elementos criados
                
xmlAutor.InnerText = this.TextBox1.Text.Trim();
                
xmlTitulo.InnerText = this.TextBox2.Text.Trim();
                
xmlConteudo.InnerText = this.TextBox3.Text.Trim();
Agora incluímos os elementos criados ao nó poemas usando o método AppendChild que inclui um novo nó ao fim da lista de nó filhos para o nó atual.
               
//inclui os elementos no novo nó
                
novoelemento.AppendChild(xmlAutor);
                
novoelemento.AppendChild(xmlTitulo);
                
novoelemento.AppendChild(xmlConteudo);
Incluímos então o novo nó poemas ao nó raiz do documento XML.
                
//inclui o novo elemento no XML
           
    xmldoc.DocumentElement.AppendChild(novoelemento);
Finalmente salvamos o documento XML para o arquivo especificado e exibimos os dados no GridView.
	//Salva a inclusão no arquivo 
XML
                
xmldoc.Save(Server.MapPath(@"App_Data\poemas.xml"));
                
this.Session["Incluir"] = 0;
                
//exibe os dados no GridView
             
  carregaDadosXML();
Alterando dados
Veremos agora o código do evento Click do botão Alterar:
|    protected void btnAlterar_Click(object sender, EventArgs e)
    {
        if (selectIndex == -1)
        {
            this.RegisterClientScriptBlock("alertmessage", "<script>alert('Selecione um item para alteração.')</script>");
        }
        else
        {
            XmlDocument xmldoc = new XmlDocument();
            xmldoc.Load(Server.MapPath(@"App_Data\poemas.xml"));
            
            XmlNode xmlnode = xmldoc.DocumentElement.ChildNodes.Item(selectIndex);            xmlnode["autor"].InnerText = this.TextBox1.Text.Trim();
            xmlnode["titulo"].InnerText = this.TextBox2.Text.Trim();
            xmlnode["conteudo"].InnerText = this.TextBox3.Text.Trim();xmldoc.Save(Server.MapPath(@"App_Data\poemas.xml"));             carregaDadosXML();
        }
    } | 
Aqui destacamos o uso da propriedade DocumentElement que obtêm a raiz XmlElement para o documento; e a propriedade ChildNodes que contêm uma lista de nós de todos os filhos os elementos.
Estamos obtendo os valores para os elementos do índice indicado que é capturado a partir da seleção de uma linha do controle GridView.
Para obter o valor do índice definimos a propriedade selectIndex conforme o código abaixo:
|  internal int selectIndex
    {
        get
        {
            if (this.Session["Page_selectIndex"] == null)
                return -1;
                return Int32.Parse(this.Session["Page_selectIndex"].ToString());
        }
        set
        {
            this.Session["Page_selectIndex"] = value;
        }
    } | 
A palavra-chave Internal é um modificador de acesso para tipos de membros que são acessíveis somente no mesmo assembly. Em C# as classes que não possuírem uma modificador de acesso, são por default internal.
Com isso estamos obtendo o valor do índice selecionado a partir do controle GridView ou retornando o valor -1 e atribuindo o valor a partir da sessão.
Após obter os valores das caixas de texto salvamos o XML para o documento poemas.xml:
xmldoc.Save(Server.MapPath(@"App_Data\poemas.xml"));
Para obter o índice atual do GridView quando o usuário selecionar uma linha temos que definir o código abaixo no evento SelectedIndexChanged:
|   protected void GridView1_SelectedIndexChanged(object sender, EventArgs e)
    {
        selectIndex = this.GridView1.SelectedIndex;
        procuraDadosXml(selectIndex);
        this.Session["Incluir"] = 0;
    } | 
Note que chamamos a rotina procuraDadosXml(indice) passando o índice selecionado. É esta rotina que irá buscar os dados no documento poemas.xml exibindo-os nas caixas de texto do formulário. Veja o código da rotina:
|   private void procuraDadosXml(int selectedIndex)
    {
        XmlDocument xmldoc = new XmlDocument();
        xmldoc.Load(Server.MapPath(@"App_Data\poemas.xml"));
        XmlNodeList xmlnodelist = xmldoc.DocumentElement.ChildNodes;       
        XmlNode xmlnode = xmlnodelist.Item(selectedIndex);
        this.TextBox1.Text = xmlnode["autor"].InnerText;
        this.TextBox2.Text = xmlnode["titulo"].InnerText;
        this.TextBox3.Text = xmlnode["conteudo"].InnerText;
    } | 
A classe XmlNodeList representa uma coleção ordenada de nós. Estamos obtendo a coleção de nós do documento XML e em seguida selecionando o nó referente ao índice selecionado.
Quando o usuário clica no botão Alterar os novos valores são salvos para o arquivo XML:
xmldoc.Save(Server.MapPath(@"App_Data\poemas.xml"));
Excluindo dados
O código associado ao evento Click do botão Excluir é dado a seguir:
|    protected void btnExcluir_Click(object sender, EventArgs e)
    {
        if (selectIndex == -1)
        {
           this.RegisterClientScriptBlock("alertmessage", "<script>alert('Selecione um item para excluir.')</script>");
        }
        else
        {
            XmlDocument xmldoc = new XmlDocument();
            xmldoc.Load(Server.MapPath(@"App_Data\poemas.xml"));
            XmlNode xmlnode = xmldoc.DocumentElement.ChildNodes.Item(selectIndex);
            xmlnode.ParentNode.RemoveChild(xmlnode);
            xmldoc.Save(Server.MapPath(@"App_Data\poemas.xml"));            carregaDadosXML();
            this.TextBox1.Text = "";
            this.TextBox2.Text = "";
            this.TextBox3.Text = "";
        }
    }
 | 
Para excluir os dados de um nó XML usamos o método RemoveChild() passando o nó desejado.
Dessa forma temos a implementação de todas as rotinas necessárias para gerenciar as informações de um arquivo XML. Talvez a interface pudesse ser melhorada e um tratamento de erros decente pudesse ser implementado. Tarefas que deixo a seu cargo para exercitar o aprendizado.
Veja abaixo a figura do web site em execução e a estrutura do arquivo poemas.xml após algumas inclusões:
|  | <?xml version="1.0" encoding="utf-8"?>
<poetas>
  <poemas>
    <autor>Fernando Pessoa</autor>
    <titulo>Tabacaria</titulo>
    <conteudo>Não sou nada.
Nunca serei nada.
Não posso querer ser nada.
À parte isso, tenho em mim todos os sonhos do mundo.</conteudo>
  </poemas>
  <poemas>
    <autor>Fernando Pessoa</autor>
    <titulo>poesias coligidas</titulo>
    <conteudo>Eu amo tudo o que foi,
Tudo o que já não é,
A dor que já me não dói,
A antiga e errônea fé,
O ontem que dor deixou,
O que deixou alegria
Só porque foi, e voou
E hoje é já outro dia.</conteudo>
  </poemas>
</poetas>
 | 
Pegue o projeto completo aqui : manuXML.zip
manuXML.zip
Eu sei é apenas ASP .NET, mas eu
gosto...
Referências:
José Carlos Macoratti