Maiquel Postado 13 Abril Postado 13 Abril 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: 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. 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 []. 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 Juliomar Marchetti Postado 14 Abril Consultores Postado 14 Abril Validou seu código? não é possível usar em lazarus, nem em versões inferiores ao 10.3 Juliomar Marchetti Ajude o Projeto ACBr crescer - Seja Pro discord: juliomar telegram: juliomar e-mail: [email protected] http://www.juliomarmarchetti.com.br 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 !!
Maiquel Postado 14 Abril Autor Postado 14 Abril 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 Juliomar Marchetti Postado 14 Abril Consultores Postado 14 Abril 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 Juliomar Marchetti Ajude o Projeto ACBr crescer - Seja Pro discord: juliomar telegram: juliomar e-mail: [email protected] http://www.juliomarmarchetti.com.br 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 !!
Maiquel Postado 14 Abril Autor Postado 14 Abril 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 Juliomar Marchetti Postado 15 Abril Consultores Postado 15 Abril 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 Juliomar Marchetti Ajude o Projeto ACBr crescer - Seja Pro discord: juliomar telegram: juliomar e-mail: [email protected] http://www.juliomarmarchetti.com.br 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 !!
Maiquel Postado 15 Abril Autor Postado 15 Abril @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"
Consultores Juliomar Marchetti Postado 15 Abril Consultores Postado 15 Abril Anexe a unit Juliomar Marchetti Ajude o Projeto ACBr crescer - Seja Pro discord: juliomar telegram: juliomar e-mail: [email protected] http://www.juliomarmarchetti.com.br 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 !!
Maiquel Postado 16 Abril Autor Postado 16 Abril Olá @Juliomar Marchetti Segue unit: 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"
Maiquel Postado 22 Abril Autor Postado 22 Abril Olá. Alguma novidade @Juliomar Marchetti Precisamos dessa alteração. Maiquel Parisotto "Ora, a fé é o firme fundamento das coisas que se esperam, e a prova das coisas que se não vêem"
Maiquel Postado 30 Abril Autor Postado 30 Abril @Juliomar Marchetti Precisamos da alteração, sabe se vai ser disponibilizada? Maiquel Parisotto "Ora, a fé é o firme fundamento das coisas que se esperam, e a prova das coisas que se não vêem"
Recommended Posts
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 contaEntrar
Já tem uma conta? Faça o login.
Entrar Agora