quinta-feira, 26 de agosto de 2010

Busca de CEP com o Lazarus - Parte 3 - XML Document

Na postagem anterior aprendemos como usar a classe THTTPSend da biblioteca synapse para criar consultar o serviço de consulta de cep e receber um xml com as informações sobre o endereço.

Hoje vamos ver como criar uma classe para encapsular todo esse mecanismo e fazer com que os atributos dessa classe sejam os dados do endereço.

Primeiro de tudo vamos criar um arquivo chamado LazCep.pas e a classe LazCep. Repare nas units que temos que adicionar no uses.

uses
  Classes, SysUtils, DOM, XMLRead, httpsend, dialogs;

type

         { TLazCep }

         TLazCep = class
         private

                FXML: DOMString;
                FCEP: DOMString;

                FXMLDoc: TXMLDocument;

                //sets
                procedure SetCep(const vCep: DOMString);

                //gets
                function GetCep: DOMString;
                function GetLogradouro: DOMString;
                function GetCidade: DOMString;
                function GetEstado: DOMString;
                function GetBairro: DOMString;
                function GetIBGEEstado: DOMString;
                function GetIBGECidade: DOMString;
                function GetIBGEVerificador: DOMString;

                //internal functions
                function GetXmlNode(const vNodeName: DOMString): DOMString;
         public
               constructor Create;
               destructor Destroy;
               procedure Consultar;
               function GetXML: DOMString;
               property Cep: DOMString read GetCep write SetCep;
               property Logradouro: DOMString read GetLogradouro;
               property Cidade: DOMString read GetCidade;
               property Estado: DOMString read GetEstado;
               property Bairro: DOMString read GetBairro;
               property IBGEEstado:DOMString read GetIBGEEstado;
               property IBGECidade:DOMString read GetIBGECidade;
               property IBGEVerificador: DOMString read GetIBGEVerificador;

         end; 

Feito isso pressione shift+ctrl+c para criar o esqueleto da classe. Implemente a classe de modo que a propriedade CEP seja um simples Get e Set, mas dê atenção especial aos métodos consultar e GetXmlNode.


{ TLazCep }

procedure TLazCep.SetCep(const vCep: DOMString);
begin
     FCEP:=vCep;
end;

function TLazCep.GetCep: DOMString;
begin
     Result := FCEP;
end;

function TLazCep.GetLogradouro: DOMString;
begin
     Result := GetXmlNode('logradouro');
end;

function TLazCep.GetCidade: DOMString;
begin
    Result := GetXmlNode('cidade');
end;

function TLazCep.GetEstado: DOMString;
begin
    Result := GetXmlNode('uf');
end;

function TLazCep.GetBairro: DOMString;
begin
   Result := GetXmlNode('bairro');
end;

function TLazCep.GetIBGEEstado: DOMString;
begin
    Result := GetXmlNode('ibge_uf');
end;

function TLazCep.GetIBGECidade: DOMString;
begin
    Result := GetXmlNode('ibge_municipio');
end;

function TLazCep.GetIBGEVerificador: DOMString;
begin
     Result := GetXmlNode('ibge_municipio_verificador');
end;

function TLazCep.GetXmlNode(const vNodeName: DOMString): DOMString;
begin

  Result := FXMLDoc.FindNode('webservicecep').FindNode('retorno').FindNode(vNodeName).TextContent;

end;

constructor TLazCep.Create;
begin
  //constructor
end;

destructor TLazCep.Destroy;
begin
     //destructor
end;

procedure TLazCep.Consultar;
var

    host: string;

    {streams que conterão o resultado do site}
    Response: TMemoryStream;
    S: TStringStream;

begin

  host := 'http://www.buscarcep.com.br/?' + 'cep='+FCEP+'&formato=xml&chave='+UrlEncode('1Z3s3/2ZjDt.dcyiLuBy/6mitlDNgt/');

  {destruindo o XMLDoc caso já exista}
  if(FXMLDoc <> nil) then
  try
       FXMLDoc.Free;
  except
  end;

  FXMLDoc := nil;

  try

    {criando os streams que conterão o resultado do site}
    Response:=TMemoryStream.Create;

    {consultando com httpGetText ou httpGetBinary}
    if HttpGetBinary(host, Response) then
    begin

         {posiciona o stream de resposta no inicio}
         Response.Seek(0, soFromBeginning);
         {Cria e Lê o documento XML a partir da stream}
         ReadXMLFile(FXMLDoc, Response);
         {posiciona o stream de resposta no inicio novamente}
         Response.Seek(0, soFromBeginning);
         {Copia o stream de resposta para um string stream, recém criado, para extrair dele uma string}
         S := TStringStream.Create(FXML);
         S.CopyFrom(Response, Response.Size-1);
         s.Seek(0, soFromBeginning);
         {atribui o conteudo todo a propriedade XML da classe, para termos acesso ao xml de retorno}
         FXML:= s.DataString;

         {coloco o encoding desejado}
         FXMLDoc.Encoding:='iso-8859-1';

    end;
  finally
    Response.Free;
    S.Free;
  end;
end;  



function TLazCep.GetXML: DOMString;
begin
  Result := FXML;
end;


end.    

Na seção implementation dessa unit colocamos o método estático que criamos na primeira parte desse tutorial, para encoding de URL.


O ponto principal aqui é o método Consultar. Repare que criamos um FXMLDoc: TXMLDocument como campo privado da classe TLazCep, certo? Esse atributo será destruido e recriado toda vez que se consultar um CEP, para limpar a estrutura XML que ele já tinha.


Logo depois criamos um memory stream, mas pode ser um string stream ou mesmo um stringList, dependendo de como você quer fazer


Posicionamos esse stream no início (não precisa fazer isso se for uma string), criamos e alimentamos o XMLDocument e ainda extraimos o conteudo do XML para um atributo da classe, para consulta ou conferência.


Depois disso a function GetXmlNode traz o conteudo do nó correto do XML, que tem o nome do dado que queremos.

Result := FXMLDoc.FindNode('webservicecep').FindNode('retorno').FindNode(vNodeName).TextContent; 

Logo depois disso podemos usar GetXmlNode para cada um dos gets das propriedades da classe. Todas são somente leitura, exceto a CEP, e todas tem um get como este:

function TLazCep.GetLogradouro: DOMString;
begin
     Result := GetXmlNode('logradouro');
end;  

Exceto a CEP, já que é por ela que começamos.


Como vimos, obrigatoriamente um TXMLDoc deve ser criado pelo método estático global AKA procedure ReadXMLFile, que cria o objeto XMLDoc e popula seu conteudo através de uma stream. Enquanto o XMLDoc está na unit DOM, a procedure ReadXmlFile está na unit XMLRead.


Repare também que não fizemos o request HTTP com o HTTPSend "na unha", mas usamos a procedure HTTPGetBinary da unit HTTPSend que faz exatamente o que fizemos no último tutorial:

function HttpGetBinary(const URL: string; const Response: TStream): Boolean;
var
  HTTP: THTTPSend;
begin
  HTTP := THTTPSend.Create;
  try
    Result := HTTP.HTTPMethod('GET', URL);
    if Result then
    begin
      Response.Seek(0, soFromBeginning);
      Response.CopyFrom(HTTP.Document, 0);
    end;
  finally
    HTTP.Free;
  end;
end; 

Até agora usamos o método HTTPGetBinary em conjunto com MemoryStreams. Podemos usar HTTPGetText em conjunto com StringLists, conforme segue:

procedure TLazCep.ConsultarString;
var
    host: string;

    {StringList que conterão o resultado do site}
    Response: TStringList;
    S: TStringStream;

begin

  host := 'http://www.buscarcep.com.br/?' + 'cep='+FCEP+'&formato=xml&chave='+UrlEncode('1Z3s3/2ZjDt.dcyiLuBy/6mitlDNgt/');

  {destruindo o XMLDoc caso já exista}
  if(FXMLDoc <> nil) then
  try
       FXMLDoc.Free;
  except
  end;

  FXMLDoc := nil;

  try

    {criando o stringlist que conterão o resultado do site}
    Response:=TStringList.Create;

    {consultando com httpGetText ou httpGetBinary}
    if HttpGetText(host, Response) then
    begin

         {capturamos em uma string o conteudo do response}
         FXML:=Response.Text;
         {criamos uma string stream a partir dele para alimentar o XMLDoc}
         S := TStringStream.Create(FXML);
         {Criamos e alimentamos o XMLDoc}
         ReadXMLFile(FXMLDoc, S);

         {coloco o encoding desejado}
         FXMLDoc.Encoding:='iso-8859-1';

    end;
  finally
    Response.Free;
    S.Free;
  end;
end;   

Essa classe ainda não funciona com proxy. Para funcionar com proxy precisaremos usar recursos especiais que serão vistos nos próximos tutoriais.

Outra coisa que precisamos é validar e disparar a mensagem de erro correta caso o xml venha vazio ou caso não seja possível estabelecer uma conexão.

Nos próximos artigos veremos como usar esta classe e como fazer essas alterações necessárias.

Ainda precisamos cuidar de conversões e codificações UTF-8 etc por causa de acentos e outros sinais diacríticos.


No próximo artigo faremos mais melhorias nesta classe. E no final poderemos fazer download do código fonte completo. Até lá :)


Ir Para:


Parte 1 | Parte 2 | Parte 3 | Parte 4 | Parte 5

Nenhum comentário:

Postar um comentário

Postagens populares

Marcadores

delphi (60) C# (31) poo (21) Lazarus (19) Site aos Pedaços (15) sql (13) Reflexões (10) .Net (9) Humor (9) javascript (9) ASp.Net (8) api (8) Básico (6) Programação (6) ms sql server (5) Web (4) banco de dados (4) HTML (3) PHP (3) Python (3) design patterns (3) jQuery (3) livros (3) metaprogramação (3) Ajax (2) Debug (2) Dicas Básicas Windows (2) Pascal (2) games (2) linguagem (2) música (2) singleton (2) tecnologia (2) Anime (1) Api do Windows (1) Assembly (1) Eventos (1) Experts (1) GNU (1) Inglês (1) JSON (1) SO (1) datas (1) developers (1) dicas (1) easter egg (1) firebird (1) interfaces (1) introspecção (1) memo (1) oracle (1) reflexão (1)