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.
A partir da ASP.NET 2.0 usa a nova propriedade Page.ClientScript para registrar e colocar funções JavaScript em suas páginas ASP.NET, os métodos Page.RegisterStartUpScript e Page.RegisterClientScriptBloc da versão 1.0/1.1 da plataforma .NET podem ser considerados obsoletos pois agora você tem que fornecer o conjunto de parâmetros chave/script para registrar scripts do lado do cliente. 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
Eu sei é apenas ASP .NET, mas eu gosto...
Referências:
José Carlos Macoratti