Ir para conteúdo
  • Cadastre-se

Recommended Posts

Postado

 

Foi identificado um problema no método de leitura de retorno (LerRetorno) ao consumir a API do Sicredi (E-Comm), onde o conteúdo retornado pelo WebService nem sempre segue um padrão único de estrutura JSON.

 Erro: Invalid class typecast

Problema encontrado

O método original utiliza:

TACBrJSONObject.Parse(RetWS)

Entretanto, o retorno da API pode variar entre:

  • Objeto JSON:

{ ... }
  • Array JSON:

[ { ... } ]

Quando o retorno ocorre no formato de array, o método Parse gera exceção (Invalid class typecast), pois espera um objeto JSON.

Além disso, em alguns cenários o retorno pode conter informações adicionais antes do JSON (ex: HTTP_Code=200 OK), o que também invalida o parse direto.


✅ Solução aplicada

Foi implementado um tratamento prévio da string de retorno (RetWS) antes do parse, com os seguintes ajustes:

  1. Remoção de conteúdo não JSON

    • Eliminação de prefixos como HTTP_Code=200 OK, mantendo apenas o conteúdo JSON válido.

  2. Normalização de estrutura

    • Quando o retorno é um array ([]), é extraído o primeiro objeto ({}), mantendo compatibilidade com a lógica já existente.

    • Quando necessário trabalhar com array posteriormente, o objeto é novamente encapsulado em [].

  3. Correção de gerenciamento de memória

    • Evitada a sobrescrita da variável LJsonObject com itens provenientes de TACBrJSONArray.

    • Criada variável auxiliar (LJsonItem) para manipulação dos dados do array, evitando Access Violation ao liberar memória.


🧠 Considerações

  • A alteração não impacta comportamentos anteriores, pois:

    • Mantém o fluxo original quando o retorno já está no formato esperado ({}).

    • Apenas adiciona robustez para cenários onde a API retorna [].

  • O ajuste foi realizado localmente na rotina de leitura, sem alterar a estrutura interna das classes do ACBr, preservando compatibilidade com futuras atualizações.


🚀 Resultado

  • Eliminação de erros de parse (Invalid class typecast)

  • Tratamento consistente para diferentes formatos de retorno da API

  • Correção de possíveis falhas de memória (uso indevido de Free)


Função alterada:

function TRetornoEnvio_Sicredi_APIECOMM.LerRetorno(const ARetornoWS: TACBrBoletoRetornoWS): Boolean;
var
  LJsonObject: TACBrJsonObject;
  LJsonItem: TACBrJsonObject;
  LRejeicao: TACBrBoletoRejeicao;
  LJsonBoletos: TACBrJsonArray;
  LTipoOperacao : TOperacao;
begin
  Result := True;
  LTipoOperacao := ACBrBoleto.Configuracoes.WebService.Operacao;
  ARetornoWS.HTTPResultCode := HTTPResultCode;
  ARetornoWS.JSONEnvio      := EnvWs;
  ARetornoWS.Header.Operacao := LTipoOperacao;
  if RetWS <> '' then
  begin
    var JsonStr: string;
    var Ini, Fim: Integer;

    JsonStr := RetWS;
    if Pos('[', JsonStr) > 0 then
      JsonStr := Copy(JsonStr, Pos('[', JsonStr), MaxInt);

    if (JsonStr <> '') and (JsonStr[1] = '[') then
    begin
      Ini := Pos('{', JsonStr);
      Fim := LastDelimiter('}', JsonStr);

      if (Ini > 0) and (Fim > Ini) then
        JsonStr := Copy(JsonStr, Ini, Fim - Ini + 1);
    end;
    //Retorno := ACBrBoleto.CriarRetornoWebNaLista;
    LJsonObject :=  TACBrJSONObject.Parse(JsonStr);
    try
      try
        ARetornoWS.JSON := LJsonObject.ToJSON;

        case HttpResultCode of
          400, 404 : begin
            if( LJsonObject.ValueExists('codigo') ) then
            begin
              LRejeicao            := ARetornoWS.CriarRejeicaoLista;
              LRejeicao.Codigo     := LJsonObject.AsString['codigo'];
              LRejeicao.Versao     := LJsonObject.AsString['parametro'];
              LRejeicao.Mensagem   := LJsonObject.AsString['mensagem'];
            end;
          end;
        end;
        //retorna quando tiver sucesso
        if (ARetornoWS.ListaRejeicao.Count = 0) then
        begin
          if (LTipoOperacao = tpInclui) then
          begin

            ARetornoWS.DadosRet.IDBoleto.CodBarras      := LJsonObject.AsString['codigoBarra'];
            ARetornoWS.DadosRet.IDBoleto.LinhaDig       := LJsonObject.AsString['linhaDigitavel'];
            ARetornoWS.DadosRet.IDBoleto.NossoNum       := LJsonObject.AsString['nossoNumero'];

            ARetornoWS.DadosRet.TituloRet.CodBarras     := ARetornoWS.DadosRet.IDBoleto.CodBarras;
            ARetornoWS.DadosRet.TituloRet.LinhaDig      := ARetornoWS.DadosRet.IDBoleto.LinhaDig;
            ARetornoWS.DadosRet.TituloRet.NossoNumero   := ARetornoWS.DadosRet.IDBoleto.NossoNum;

          end else
          if (LTipoOperacao in [tpConsultaDetalhe,tpConsulta]) then
          begin
            JsonStr := LJsonObject.ToJSON;

            // se não for array, vira array
            if (JsonStr <> '') and (JsonStr[1] <> '[') then
              JsonStr := '[' + JsonStr + ']';

            LJsonBoletos := TACBrJSONArray.Parse(JsonStr);
            try
              if (LJsonBoletos.Count > 0) then
              begin
                LJsonItem := LJsonBoletos.ItemAsJSONObject[0];

                ARetornoWS.DadosRet.IDBoleto.CodBarras       := '';
                ARetornoWS.DadosRet.IDBoleto.LinhaDig        := '';
                ARetornoWS.DadosRet.IDBoleto.NossoNum        := LJsonItem.AsString['nossoNumero'];
                ARetornoWS.indicadorContinuidade             := false;
                ARetornoWS.DadosRet.TituloRet.CodBarras      := ARetornoWS.DadosRet.IDBoleto.CodBarras;
                ARetornoWS.DadosRet.TituloRet.LinhaDig       := ARetornoWS.DadosRet.IDBoleto.LinhaDig;

                ARetornoWS.DadosRet.TituloRet.NossoNumero                := ARetornoWS.DadosRet.IDBoleto.NossoNum;
                ARetornoWS.DadosRet.TituloRet.Vencimento                 := DateSicrediToDateTime(LJsonItem.AsString['dataVencimento']);
                ARetornoWS.DadosRet.TituloRet.ValorDocumento             := LJsonItem.AsFloat['valor'];
                ARetornoWS.DadosRet.TituloRet.ValorAtual                 := LJsonItem.AsFloat['valor'];
                ARetornoWS.DadosRet.TituloRet.EstadoTituloCobranca       := LJsonItem.AsString['situacao'];
                ARetornoWS.DadosRet.TituloRet.SeuNumero                  := LJsonItem.AsString['seuNumero'];

                if( LJsonItem.AsString['situacao'] = C_LIQUIDADO ) or
                   ( LJsonItem.AsString['situacao'] = C_BAIXADO_POS_SOLICITACAO ) then
                begin
                  ARetornoWS.DadosRet.TituloRet.ValorPago                := LJsonItem.AsFloat['valorLiquidado'];
                  ARetornoWS.DadosRet.TituloRet.DataCredito              := DateSicrediToDateTime(LJsonItem.AsString['dataliquidacao']);
                end;

              end;
            finally
              if Assigned(LJsonBoletos) then
                LJsonBoletos.Free;
            end;
          end else
          if (LTipoOperacao = tpBaixa) then
          begin
            // não possui dados de retorno..
          end else
          if (LTipoOperacao = tpAltera) then
          begin
            // não possui dados de retorno..
          end;
        end;
      except
        Result := False;
      end;
    finally
      LJsonObject.Free;
    end
  end;
end;

Segue a Unit alterada:

ACBrBoletoRet_Sicredi_APIECOMM.pas

Maiquel Parisotto

 

"Ora, a fé é o firme fundamento das coisas que se esperam, e a prova das coisas que se não vêem"

  • Consultores
Postado

Validou seu código?

não é possível usar em lazarus, nem em versões inferiores ao 10.3

 

Consultora ACBr Pro

Juliomar Marchetti

Ajude o Projeto ACBr crescer - Seja Pro

discord: juliomar
telegram: juliomar
e-mail: [email protected]
http://www.juliomarmarchetti.com.br

 

MVP_NewLogo_100x100_Transparent-02.png
Projeto ACBr - A maior comunidade Open Source de Automação Comercial do Brasil


Participe de nosso canal no Discord e fique ainda mais próximo da Comunidade !!

Postado

Olá @Juliomar Marchetti

Validei o código no Delphi 10.4.

Aqui tudo funcionando perfeitamente. Acredito não ter problema em outras versões!

Maiquel Parisotto

 

"Ora, a fé é o firme fundamento das coisas que se esperam, e a prova das coisas que se não vêem"

  • Consultores
Postado
2 horas atrás, Maiquel disse:

Olá @Juliomar Marchetti

Validei o código no Delphi 10.4.

Aqui tudo funcionando perfeitamente. Acredito não ter problema em outras versões!

Exatamente. 
confere o que só tem no seu delphi 10.4 e remove ou ajusta para poder rodar em outros senão não poderei pensar em subir esse código

 

Consultora ACBr Pro

Juliomar Marchetti

Ajude o Projeto ACBr crescer - Seja Pro

discord: juliomar
telegram: juliomar
e-mail: [email protected]
http://www.juliomarmarchetti.com.br

 

MVP_NewLogo_100x100_Transparent-02.png
Projeto ACBr - A maior comunidade Open Source de Automação Comercial do Brasil


Participe de nosso canal no Discord e fique ainda mais próximo da Comunidade !!

Postado

Não consigo testar aqui, mas não implementei nada de diferente que não tenha nas versões inferiores, é bem exclusivo nesta função, onde hoje pessoal que usa esta com problema.

Precisaria de alguém que tenha versões anteriores para testar, mas não vejo necessidade, nesta alteração.

Somente para versãos anteriores Delphi XE7, que não poderia criar a variável em tempo de execução:
 

var JsonStr: string;

 

Maiquel Parisotto

 

"Ora, a fé é o firme fundamento das coisas que se esperam, e a prova das coisas que se não vêem"

  • Consultores
Postado
3 horas atrás, Maiquel disse:

Não consigo testar aqui, mas não implementei nada de diferente que não tenha nas versões inferiores, é bem exclusivo nesta função, onde hoje pessoal que usa esta com problema.

Precisaria de alguém que tenha versões anteriores para testar, mas não vejo necessidade, nesta alteração.

Somente para versãos anteriores Delphi XE7, que não poderia criar a variável em tempo de execução:
 

var JsonStr: string;

 

acho que pode ter mais coisas. pois a IA não validou o trecho pra funcionar em todas as versões atendidas

 

Consultora ACBr Pro

Juliomar Marchetti

Ajude o Projeto ACBr crescer - Seja Pro

discord: juliomar
telegram: juliomar
e-mail: [email protected]
http://www.juliomarmarchetti.com.br

 

MVP_NewLogo_100x100_Transparent-02.png
Projeto ACBr - A maior comunidade Open Source de Automação Comercial do Brasil


Participe de nosso canal no Discord e fique ainda mais próximo da Comunidade !!

Postado

@Juliomar Marchetti

Segue função alterada e validada em versões inferiores:
 

function TRetornoEnvio_Sicredi_APIECOMM.LerRetorno(
  const ARetornoWS: TACBrBoletoRetornoWS): Boolean;
var
  LJsonObject: TACBrJsonObject;
  LJsonItem: TACBrJsonObject;
  LRejeicao: TACBrBoletoRejeicao;
  LJsonBoletos: TACBrJsonArray;
  LTipoOperacao: TOperacao;
  JsonStr: string;
  Ini, Fim: Integer;
begin
  Result := True;

  LJsonObject := nil;
  LJsonBoletos := nil;
  LJsonItem := nil;

  LTipoOperacao := ACBrBoleto.Configuracoes.WebService.Operacao;
  ARetornoWS.HTTPResultCode := HTTPResultCode;
  ARetornoWS.JSONEnvio      := EnvWs;
  ARetornoWS.Header.Operacao := LTipoOperacao;

  if RetWS <> '' then
  begin
    JsonStr := RetWS;

    if Pos('[', JsonStr) > 0 then
      JsonStr := Copy(JsonStr, Pos('[', JsonStr), MaxInt);

    if (Length(JsonStr) > 0) and (JsonStr[1] = '[') then
    begin
      Ini := Pos('{', JsonStr);
      Fim := LastDelimiter('}', JsonStr);

      if (Ini > 0) and (Fim > Ini) then
        JsonStr := Copy(JsonStr, Ini, Fim - Ini + 1);
    end;

    LJsonObject := TACBrJSONObject.Parse(JsonStr);
    try
      try
        ARetornoWS.JSON := LJsonObject.ToJSON;

        case HttpResultCode of
          400, 404:
          begin
            if LJsonObject.ValueExists('codigo') then
            begin
              LRejeicao := ARetornoWS.CriarRejeicaoLista;
              LRejeicao.Codigo   := LJsonObject.AsString['codigo'];
              LRejeicao.Versao   := LJsonObject.AsString['parametro'];
              LRejeicao.Mensagem := LJsonObject.AsString['mensagem'];
            end;
          end;
        end;

        if (ARetornoWS.ListaRejeicao.Count = 0) then
        begin
          if (LTipoOperacao = tpInclui) then
          begin
            ARetornoWS.DadosRet.IDBoleto.CodBarras :=
              LJsonObject.AsString['codigoBarra'];
            ARetornoWS.DadosRet.IDBoleto.LinhaDig :=
              LJsonObject.AsString['linhaDigitavel'];
            ARetornoWS.DadosRet.IDBoleto.NossoNum :=
              LJsonObject.AsString['nossoNumero'];

            ARetornoWS.DadosRet.TituloRet.CodBarras :=
              ARetornoWS.DadosRet.IDBoleto.CodBarras;
            ARetornoWS.DadosRet.TituloRet.LinhaDig :=
              ARetornoWS.DadosRet.IDBoleto.LinhaDig;
            ARetornoWS.DadosRet.TituloRet.NossoNumero :=
              ARetornoWS.DadosRet.IDBoleto.NossoNum;
          end
          else if (LTipoOperacao in [tpConsultaDetalhe, tpConsulta]) then
          begin
            JsonStr := LJsonObject.ToJSON;

            if (Length(JsonStr) > 0) and (JsonStr[1] <> '[') then
              JsonStr := '[' + JsonStr + ']';

            LJsonBoletos := TACBrJSONArray.Parse(JsonStr);
            try
              if (Assigned(LJsonBoletos)) and (LJsonBoletos.Count > 0) then
              begin
                LJsonItem := LJsonBoletos.ItemAsJSONObject[0];

                ARetornoWS.DadosRet.IDBoleto.CodBarras := '';
                ARetornoWS.DadosRet.IDBoleto.LinhaDig := '';
                ARetornoWS.DadosRet.IDBoleto.NossoNum :=
                  LJsonItem.AsString['nossoNumero'];

                ARetornoWS.indicadorContinuidade := False;

                ARetornoWS.DadosRet.TituloRet.CodBarras :=
                  ARetornoWS.DadosRet.IDBoleto.CodBarras;
                ARetornoWS.DadosRet.TituloRet.LinhaDig :=
                  ARetornoWS.DadosRet.IDBoleto.LinhaDig;

                ARetornoWS.DadosRet.TituloRet.NossoNumero :=
                  ARetornoWS.DadosRet.IDBoleto.NossoNum;
                ARetornoWS.DadosRet.TituloRet.Vencimento :=
                  DateSicrediToDateTime(LJsonItem.AsString['dataVencimento']);
                ARetornoWS.DadosRet.TituloRet.ValorDocumento :=
                  LJsonItem.AsFloat['valor'];
                ARetornoWS.DadosRet.TituloRet.ValorAtual :=
                  LJsonItem.AsFloat['valor'];
                ARetornoWS.DadosRet.TituloRet.EstadoTituloCobranca :=
                  LJsonItem.AsString['situacao'];
                ARetornoWS.DadosRet.TituloRet.SeuNumero :=
                  LJsonItem.AsString['seuNumero'];

                if (LJsonItem.AsString['situacao'] = C_LIQUIDADO) or
                   (LJsonItem.AsString['situacao'] = C_BAIXADO_POS_SOLICITACAO) then
                begin
                  ARetornoWS.DadosRet.TituloRet.ValorPago :=
                    LJsonItem.AsFloat['valorLiquidado'];
                  ARetornoWS.DadosRet.TituloRet.DataCredito :=
                    DateSicrediToDateTime(
                      LJsonItem.AsString['dataliquidacao']);
                end;
              end;
            finally
              if Assigned(LJsonBoletos) then
                FreeAndNil(LJsonBoletos);
            end;
          end;
        end;
      except
        Result := False;
      end;
    finally
      if Assigned(LJsonObject) then
        FreeAndNil(LJsonObject);
    end;
  end;
end;

 

Maiquel Parisotto

 

"Ora, a fé é o firme fundamento das coisas que se esperam, e a prova das coisas que se não vêem"

  • 2 semanas depois ...

Crie uma conta ou entre para comentar

Você precisar ser um membro para fazer um comentário

Criar uma conta

Crie uma nova conta em nossa comunidade. É fácil!

Crie uma nova conta

Entrar

Já tem uma conta? Faça o login.

Entrar Agora
×
×
  • Criar Novo...

Informação Importante

Colocamos cookies em seu dispositivo para ajudar a tornar este site melhor. Você pode ajustar suas configurações de cookies, caso contrário, assumiremos que você está bem para continuar.