Desenvolvimento - XML
Usando arquivos XML no formato simples
por Eduardo Kaiser
Olá pessoal, muitos de vocês que estão começando em .NET e vieram do Visual Basic 6.0 procuram pela API que permite gravar e ler arquivos .INI. Usávamos este tipo de arquivo no VB6 pois eram a melhor e mais fácil alternativa para guardar configurações de usuário para os nossos aplicativos.
Em C#.NET temos uma vasta biblioteca que fazem este tipo de serviço BEM mais fácil. Então eu decidi mostrar uma classe que eu desenvolvi durante meu aprendizado para salvar e ler configurações de arquivos XML básicos (quando eu digo básico, quero dizer que não tem muitas estruturas).
Vejamos o que seria um arquivo básico:
|
<?xml version="1.0" encoding="utf-8"?> <CONFIG> <Comum> <!-- atualizadoem DEVE ser a primeira chave deste grupo!!!--> <ATUALIZADOEM required="1">19/01/2011 15:00</ATUALIZADOEM> <drvTemplates required="1">\\10.32.8.1\FERRAMENTAS\Templates\Aprovados\</drvTemplates> <BD_DATASOURCE required="1">db554-srv001</BD_DATASOURCE> <BDCOLAB_DATASOURCE required="1">ww553-wrk002</BDCOLAB_DATASOURCE> <extensao required="1">.xls</extensao> <picmaxHei required="1">700</picmaxHei> <picmaxWid>486</picmaxWid> <lastUser>12345678</lastUser> <showexcel>false</showexcel> </Comum> </CONFIG> |
Tabela 1 – Conteúdo exemplo do arquivo XML
A classe que criei abaixo faz o controle do arquivo acima. Para isto você verá que vamos usar o SYSTEM.XML.LINQ. Esta classe nos traz muita facilidade para mecher com arquivos do tipo XML.
O código completo da classe clXML está abaixo.
A biblioteca KaiserSoft.Facilities é uma outra classe que eu criei que contém métodos estáticos que são usados sempre e por vários aplicativos mas que para esta classe XML não tem importância, então, onde você encontrar no código (clFacilidades.Reportprg) pode apagar pois é só um log e onde você encontrar (clFacilidades.Reportprg), pode trocar por MessageBox.Show(“....”);
|
using System; using System.Collections.Generic; using System.Xml.Linq; using KaiserSoft.Facilities; namespace System { class clXML { private XDocument xmlDoc = null; private bool arquivoAberto; private string Arquivo; public clXML() { clFacilidades.Reportprg("clXML", "clXML", "Constructor"); xmlDoc = new XDocument(); } ~clXML() { clFacilidades.Reportprg("clXML", "~clXML", "Destructor"); // o destrutor está aqui para fechar o arquivo aberto, assim o usuario não precisa chamar nenhum passo para fazer isto xmlDoc = null; arquivoAberto = false; } public void Dispose() { clFacilidades.Reportprg("clXML", "Dispose", ""); xmlDoc = null; arquivoAberto = false; } private bool OpenFile(string Arq) { clFacilidades.Reportprg("clXML", "OpenFile", @Arq); if (!arquivoAberto) { Arquivo = Arq; arquivoAberto = true; try { xmlDoc = XDocument.Load(Arq); } catch (Exception ex) { clFacilidades.Report("Erro ao abrir o arquivo XML: " + Arq, "clXML", "OpenFile", ex.Message.ToString()); arquivoAberto = false; } } return arquivoAberto; } public string getValue(string Group, string Key) { clFacilidades.Reportprg("clXML", "getValue", "Group,Key"); if (!arquivoAberto) return ""; try { return xmlDoc.Root.Element(Group).Element(Key).Value.ToString(); } catch (Exception) { return ""; }
} public string getValue(string Group, string Key, string Arq) { clFacilidades.Reportprg("clXML", "getValue", "Arq:" + Arq); // chamar esta rotina na primeira vez, depois usar somente getValue if (arquivoAberto) { xmlDoc = null; arquivoAberto = false; } if (OpenFile(Arq) == false) return ""; return getValue(Group,Key); } public bool getNextNode(string Group, ref string Key, ref string Value, ref string Attrib) { clFacilidades.Reportprg("clXML", "getNextNode", "G:" + Group + " V:" + Value + "A:" + Value); XElement no; if (!arquivoAberto) { clFacilidades.Report("O arquivo XML não foi aberto ainda!", "clXML", "getNextNode", "Attrib"); Attrib = ""; return true; } if (Key == string.Empty) { // pega a primeira chave disponível dentro deste grupo no = null; } else no = (XElement)xmlDoc.Root.Element(Group).Element(Key).NextNode; Attrib = Value = ""; if (no != null) { Key = no.Name.ToString(); if (no.HasAttributes) { Attrib = no.Attribute("required").Value.ToString(); Value = no.Value.ToString(); } } else return true; // acabaram os nós return false; } public bool getNextNode(string Group, ref string Key, ref string Value) { clFacilidades.Reportprg("clXML", "getNextNode", "G:" + Group + " Value:" + Value); XElement no; if (!arquivoAberto) { clFacilidades.Report("O arquivo XML não foi aberto ainda!", "clXML", "getNextNode", ""); return true; } if (Key == string.Empty) { // pega a primeira chave disponível dentro deste grupo no = null; } else no = (XElement)xmlDoc.Root.Element(Group).Element(Key).NextNode; Value = ""; if (no != null) { Key = no.Name.ToString(); Value = no.Value.ToString(); } else return true; // acabaram os nós return false; } public void setValue(string Group, string Key, string Value, string Arq) { clFacilidades.Reportprg("clXML", "setValue", "Arq:" + Arq); if (!arquivoAberto) if (OpenFile(Arq) == false) return; setValue(Group, Key, Value); } public void setValue(string Group, string Key, string Value) { clFacilidades.Reportprg("clXML", "setValue", "Group,Key,Value"); // precisa verificar se o grupo existe, senão cria ele if (CreateGroup(Group, Key, Value)) { xmlDoc.Root.Element(Group).Element(Key).Value = Value; } } public bool CreateGroup(string Group, string Key, string Value) { clFacilidades.Reportprg("clXML", "createGroup", "Grupo:" + Group); if (!arquivoAberto) return false; string txt; try { txt = xmlDoc.Root.Element(Group).Name.ToString(); } catch (Exception) { xmlDoc.Root.Add(new XElement(Group, new XElement(Key,Value))); // cria o grupo e já cria a Key return false; } // neste ponto, o Grupo existe, porém a Key pode ser nova try { txt = xmlDoc.Root.Element(Group).Element(Key).Name.ToString(); } catch (Exception) { //xmlDoc.Root.Add(xmlDoc.Root.Element(Group), new XElement(Key, Value)); xmlDoc.Root.Element(Group).Add(new XElement(Key, Value)); return false; } // neste ponto, o grupo existe e a key também, então informa que precisa só escrever o valor return true; } public bool saveFile(string SaveAs) { clFacilidades.Reportprg("clXML", "saveFile", "SaveAs=" + SaveAs); try { if (SaveAs != string.Empty) xmlDoc.Save(@SaveAs); else xmlDoc.Save(@Arquivo); } catch (Exception ex) { clFacilidades.Report("Erro ao salvar o arquivo XML", "clXML", "saveFile", ex.Message.ToString()); return false; } return true; } } } |
Tabela 2 – Código fonte da classe clXML
Como usar a classe acima:
Para utilizar a classe acima, aqui vai um bom exemplo. Na rotina de start-up do seu aplicativo (normalmente em algum form_Load), vamos colocar o código abaixo.
NOTA: Perceba que a rotina abaixo faz uso da parte de Settings do seu projeto, então você precisa criar as chaves nesta configuração para que a rotina possa ler e armazenar os valores.
Explicando o que ele faz:
Quando o aplicativo é instalado, o arquivo config.xml (que faz parte do pacote de instalação), é copiado por cima do arquivo onde o aplicativo foi instalado.
Ao executar o seu aplicativo, a rotina abaixo vai comparar as diferenças entre este arquivo novo e o arquivo que se encontra no diretório de documentos do aplicativo (este diretório é criado automaticamente caso não exista e o arquivo config.xml novo é copiado dentro dele), que fica no Documents and Settings\NomedoAplicativo
Sempre que seu instalador for modificado, o arquivo config.xml é copiado novametne e a rotina abaixo só vai copiar os dados que estiverem marcados como required. Isto significa que, por exemplo, o último usuário que logou no sistema não será sobrescrito, apenas serão sobrescritos os dados que você marcou como dados que o programa depende para funcionar e que o usuário normalmente não altera (required=”1”). Desta forma, se a localização do banco de dados foi alterada, por exemplo, na próxima instalação este dado será atualizado automaticamente.
|
#region CONFIG.XML float a; bool b; clXML xml = new clXML(); clXML xml2 = new clXML(); string txt1, txt2 = ""; Properties.Settings.Default.drvProgramData = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments) + "\\" + System.Windows.Forms.Application.ProductName + "\\"; // verifica se a data do config.xml instalado é <> do que o do usuário, se sim, copia por cima o arquivo txt1 = xml2.getValue("Comum", "ATUALIZADOEM", Properties.Settings.Default.drv + "Config.xml"); txt2 = xml.getValue("Comum", "ATUALIZADOEM", Properties.Settings.Default.drvProgramData + "Config.xml"); if (txt1 != txt2) { // cria a pasta try { Directory.CreateDirectory(Properties.Settings.Default.drvProgramData); // se o arquivo ainda não existe, então só copia o novo if (!File.Exists(Properties.Settings.Default.drvProgramData + "Config.xml")) File.Copy(Properties.Settings.Default.drv + "Config.xml", Properties.Settings.Default.drvProgramData + "Config.xml", true); else { // como o arquivo já existe, então copia somente as chaves dentro de comum que contiverem o atributo de obrigatório string Key = "ATUALIZADOEM"; string Value = ""; string Attrib = ""; // já grava a primeira informação que é a data xml.setValue("Comum", Key, txt1); while (!xml2.getNextNode("Comum", ref Key, ref Value, ref Attrib)) { // se Key = "" é porque esta chave não contém o atributo de cópia obrigatória, então não copia if (Attrib != "") xml.setValue("Comum", Key, Value); } xml.saveFile(""); } } catch (Exception ex) { MessageBox.Show(ex.Message.ToString(), "Erro ao copiar arquivo de definições!"); this.Dispose(); return; } } xml2.Dispose(); #endregion // pega a localização do servidor de banco de dados txt1 = ""; txt1 = xml.getValue("Comum", "BD_DATASOURCE", Properties.Settings.Default.drvProgramData + "Config.xml"); if (txt1 != "") Properties.Settings.Default.BD_DATASOURCE = txt1; string lastUser = xml.getValue("Comum", "lastUser"); txt1 = xml.getValue("Comum", "drvTemplates"); if (txt1 != "") Properties.Settings.Default.drvTemplates = txt1 +System.Windows.Forms.Application.ProductName + "\\"; txt1 = xml.getValue("Comum", "picmaxWid"); float.TryParse(txt1, out a); Properties.Settings.Default.picmaxWid = a; txt1 = xml.getValue("Comum", "picmaxHei"); float.TryParse(txt1, out a); Properties.Settings.Default.picmaxHei = a; txt1 = xml.getValue("Comum", "extensao"); if (txt1 != "") Properties.Settings.Default.extensao = txt1; txt1 = xml.getValue("Comum", "showexcel"); bool.TryParse(txt1, out b); if (txt1 != "") Properties.Settings.Default.showExcel = b; xml.Dispose(); xml = null; |
Tabela 3 – Exemplo de leitura dos dados do arquivo XML
E agora, um exemplo de como salvar uma informação (desde que não seja required), no arquivo XML:
|
// SALVA ULTIMO USUÁRIO LOGADO xml = new clXML(); xml.setValue("Comum", "lastUser", Properties.Settings.Default.userlogin, Properties.Settings.Default.drvProgramData + "Config.xml"); xml.setValue("Comum", "appdrv", Application.ExecutablePath); xml.saveFile(""); xml.Dispose(); xml = null; |
Tabela 4 – Exemplo de escrita de dados no arquivo XML
Note que no exemplo da tabela 4, o programa está mandando salvar o valor para o campo appdrv. Se você notar no arquivo XML da tabela 1, este campo não existe. Neste caso a classe clXML vai se encarregar de criar o campo e colocar seu valor. Isto é válido também se você mudar o grupo “Comum” para outro nome, a classe se encarrega de criar o grupo caso ele não exista, evitando erros.
Este é o final do nosso artigo. A classe clXML acima será utilizada em outros artigos que eu venha a postar, então guarde-a com carinho J
Quaisquer dúvidas, sugestões ou críticas serão bem vindas e poderão ser feitas nos comentários deste artigo. Até a próxima!






