Desenvolvimento - XMLFeed de artigos deste autor

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!

Eduardo Kaiser

Eduardo Kaiser - Analista de Sistemas. Desenvolvimento de ferramentas e sistemas. Planejamento, definição, estruturação, desenvolvimento, testes, otimização e validação de projetos. Conhecimentos Visual Basic 3.0, 6.0, C++, VBScript, VBA, SQL Server, C#.NET, ASP.NET, Comunicação e protocolos RS232/485, GPIB, OPC, Xcode.


Comentários

blog comments powered by Disqus