Jump to content

2 Dia do ACBr

Pré-Venda com Desconto de R$100,00
INSCREVA-SE

Nova Loja Oficial
loja.projetoacbr.com.br
Ajude o projeto a crescer, com estilo

Comprar

Balança SM100 performance surpreendente

Tecnologia Japonesa   Teclado e Visor resistentes a água
Consumo inteligente de etiquetas   Baixo custo de manutenção
Comunicação Ethernet e WIFI independentes

Saiba mais

Impressora de Etiquetas ELGIN - L42 PRO

Protocolos PPLA, PPLB, ZPL, EPL (automático)
Porta USB padrão Opcionais: Ethernet, Serial, Paralela
Sensor de Etiquetas Móvel Garantia de 18 meses

Saiba mais

AlexBecker

pegar valor de array json dentro de um array json - ifood

Recommended Posts

Olá a todos! estou integrando a api do ifood ao retaguarda e com todas as informações de consumo da API ok, porém estou tendo uma dificuldade em pegar 'SUBDADOS' dentro do array json:

[{"name":"PEDIDO DE TESTE - Pastel de palmito","quantity":1,"price":5.0,"subItemsPrice":0,"totalPrice":5.0,"discount":0.0,"addition":0.0,"externalCode":"123"},{"name":"PEDIDO DE TESTE - Item teste","quantity":1,"price":10.0,"subItemsPrice":0,"totalPrice":10.0,"discount":0.0,"addition":0.0,"externalCode":"789"},{"name":"PEDIDO DE TESTE - X-burguer bacon","quantity":1,"price":8.0,"subItemsPrice":0,"totalPrice":8.0,"discount":0.0,"addition":0.0,"externalCode":"7"},{"name":"PEDIDO DE TESTE - Item teste","quantity":1,"price":10.0,"subItemsPrice":0,"totalPrice":10.0,"discount":0.0,"addition":0.0,"externalCode":"789","observations":"COM OBSERVAO"},{"name":"PEDIDO DE TESTE - Item teste","quantity":1,"price":10.0,"subItemsPrice":0,"totalPrice":10.0,"discount":0.0,"addition":0.0,"externalCode":"789","observations":"SEM OBSERVAO"},{"name":"PEDIDO DE TESTE - X-burguer bacon","quantity":1,"price":8.0,"subItemsPrice":1.5,"totalPrice":9.5,"discount":0.0,"addition":0.0,"externalCode":"7","subItems":[{"name":"Alface","quantity":1,"price":1.5,"totalPrice":1.5,"discount":0.0,"addition":0.0,"externalCode":"13"}],"observations":"TESTE COM ALFACE"},{"name":"PEDIDO DE TESTE - GRANDE","quantity":1,"price":0.0,"subItemsPrice":48.0,"totalPrice":48.0,"discount":0.0,"addition":0.0,"externalCode":"25","subItems":[{"name":"BORDA DE CATURPIRY","quantity":1,"price":6.0,"totalPrice":6.0,"discount":0.0,"addition":0.0,"externalCode":"2000"},{"name":"PALMITO","quantity":1,"price":42.0,"totalPrice":42.0,"discount":0.0,"addition":0.0,"externalCode":"33"}],"observations":"TESTE DE PIZZA"},{"name":"PEDIDO DE TESTE - GRANDE 3 SABORES","quantity":1,"price":0.0,"subItemsPrice":55.98,"totalPrice":55.98,"discount":0.0,"addition":0.0,"externalCode":"25","subItems":[{"name":"BORDA DE CHEDDAR","quantity":1,"price":6.0,"totalPrice":6.0,"discount":0.0,"addition":0.0,"externalCode":"3000"},{"name":"1/3 3 QUEIJOS","quantity":1,"price":16.66,"totalPrice":16.66,"discount":0.0,"addition":0.0,"externalCode":"30"},{"name":"1/3 CALABRESA","quantity":1,"price":15.0,"totalPrice":16.66,"discount":0.0,"addition":1.66,"externalCode":"31"},{"name":"1/3 MUSSARELA","quantity":1,"price":10.0,"totalPrice":16.66,"discount":0.0,"addition":6.66,"externalCode":"32"}]}]

para cada item tenho OU NÃO um subitem, para cada item tenho OU NÃO uma observação, estou usando a função jsontodataset mostrada em exemplo aqui no forum ACBR por um colega que funciona muito bem porém quando uso o comando: 

    vItensJson           := ObjetoJson.GetValue('items').ToString;

    JsonArrayToDataset(tbItems, vItensJson);

a tabela é gerada porém somente com a parte dos itens, o campo subitens não é gerado e assim não tenho como pegar a informacao do json subitens de cada item assim como a observação de cada item, alguém poderia por gentileza ajudar com isto? tentei alguns exemplos de parser da internet porém sem sucesso... desde já agradeço.

 

Share this post


Link to post
Share on other sites

JsonToDataset não leva em consideração o "detail" de um objeto ou array do json...

A melhor forma de você tratar isso é convertendo um JSON para um Object usando Generics 

Em resumo, você cria uma classe no Delphi igual ao JSON que recebe do iFood.

TPayments = class
  private
    Fname:  string;
    Fcode:  string;
    Fvalue: Extended;
  public
    property name:  string   read Fname  write Fname;
    property code:  string   read Fcode  write Fcode;
    property value: Extended read Fvalue write Fvalue;
  end;
  TArrayPayments = array of TPayments;  

TiFoodPedido = class
  private
    Fid:        string;
    Freference: string;
    Fpayments:  TArrayPayments;
  public
    property id:        string           read Fid        write Fid;
    property reference: string           read Freference write Freference;
    property payments:  TArrayPayments   read Fpayments  write Fpayments;
  end;
  TiFoodPedidos = TObjectList<TiFoodPedido>;

No caso do payments é um array. Mas terá casos onde serão objetos (merchant, address, customer...). Sendo assim não esqueça de criar constructor e destructor para criar e destruir esses objetos.

constructor TiFoodPedido.Create;
begin
  FCustomer        := TCustomer.Create;
  FDeliveryAddress := TDeliveryAddress.Create;
end;

destructor TiFoodPedido.Destrtoy;
begin
  FCustomer.Free;
  FDeliveryAddress.Free;
end;

Depois você pode simplesmente chamar assim:

var
PedidoiFood: TiFoodPedido;
begin
  PedidoiFood := TJSON.JsonToObject<TiFoodPedido>('JSON de retorno do iFood');
  try
    //dessa forma você consegue ler os items dentro de um array
    PedidoiFood.payments[0].name;
    PedidoiFood.payments[0].code;
    PedidoiFood.payments[0].value;    
  finally
    PedidoiFood.Free;
  end;  
end;

Caso queira adicionar mais pedidos na sua lista:

var
Pedidos:     TiFoodPedidos;
PedidoiFood: TiFoodPedido;
x: Integer;
begin
  Pedidos := TiFoodPedidos.Create;
  try
    for x := 0 to 5 do
    begin
      PedidoiFood := TJSON.JsonToObject<TiFoodPedido>('JSON de retorno do iFood');
      Pedidos.Add(PedidoiFood);
    end;
    
    //No final poderá acessar assim
    Pedidos.Items[0].payments[0].name;
  finally
    Pedidos.Free;
  end;
end;        

 

  • Like 1

Share this post


Link to post
Share on other sites

Olá muito obrigado, na verdade o problema ali é que a primeira linha dos itens não retorna a estrutura completa do objeto/array itens se o mesmo não tem subitens, pelo que vi deveria retornar mesmo em branco o array SubItems com os campos e seus valores mesmo com zero, então assim... lançando a linha: 

  vStringItem := '{"name":"ITEMARRAY","quantity":0,"price":0.0,"subItemsPrice":0.0,"totalPrice":0.0,'+
  '"discount":0.0,"addition":0.0,"externalCode":"0","subItems":[{"name":"desc","quantity":0,"price":0.0,"totalPrice":0.0,'+
  '"discount":0.0,"addition":0.0,"externalCode":"0"}],"observations":"obs"},'; 

que na verdade é o objeto do SubItems completo ai o jsontodataset reconhece o array subitens e o campo observacoes porém tenho que conseguir criar o campo manualmente pois o jsontodataset cria automaticamente um campo com 255 caracteres e não entra todo o valor, se alguém tiver um exemplo do JsonToDataset criando os campos com Text, BloB ou variável que aceite mais caracteres ajudaria bastante, encontrei um exemplo com ClientDataSet mas tá bugadasso, estudei ele, tentei alterar e adequar mas não tive sucesso nesta tarefa até agora.

Edited by AlexBecker
erro de digitacao

Share this post


Link to post
Share on other sites
5 horas atrás, AlexBecker disse:

o problema ali é que a primeira linha dos itens não retorna a estrutura completa do objeto/array itens se o mesmo não tem subitens

Sim, quando o iFood monta o JSON, eles ignoram arrays, strings, números, objetos em branco. Deixando assim o retorno somente com o que é necessário.

Como eu citei acima, aqui eu criei um unit com todos os retornos possível do JSON (property). Depois eu só converto o JSON para um ObjectList usando generics.

Fiz um exemplo bem simples, só pra mostrar que de um lado eu tenho um memo com o JSON do iFOOD e do outro o meu ObjectList.

giphy.gif

Vou deixar o código aqui.

var
MyPedidoiFood:  TiFoodPedido;
x, i: Integer;
begin
  MyPedidoiFood := TJSON.JsonToObject<TiFoodPedido>(MemoJSON.Lines.Text); //Memo com o JSON do iFood
  try
    for x := 0 to Length(MyPedidoiFood.items) - 1 do
    begin
      MemoClasseDelphi.Lines.Add('Nome mercadoria base: '+MyPedidoiFood.Items[x].name);
      for i := 0 to Length(MyPedidoiFood.items[x].subItems) - 1 do
        MemoClasseDelphi.Lines.Add('Nome subitem: '+MyPedidoiFood.Items[x].subItems[i].name);
      MemoClasseDelphi.Lines.Add('');
    end;
  finally
    MyPedidoiFood.Free;
  end;
end;

E o link do exemplo compilado caso queira testar com um JSON seu. 

5 horas atrás, AlexBecker disse:

jsontodataset cria automaticamente um campo com 255 caracteres e não entra todo o valor

Se estiver utilizando TCustomJSONDataSetAdapter. Você pode aumentar o tamanho da string por essa propriedade: StringFieldSize (antes de criar os campos).

iFood.gif

Share this post


Link to post
Share on other sites

show!  eu separei todos os arrays que retornam no json como local de entrega, formas de pagamento, em um memo também e depois disto fiz um jsontodataset, porém com o subitens estava mais dificil fazer mas a base onde consegui retirar é como no teu exemplo, looping no registro json  foi o unico jeito mesmo. Muito obrigado!

Share this post


Link to post
Share on other sites

tentei mais algumas opções e não deu certo...

segue o array contendo apenas 1 item retornado do json:

[{"name":"PEDIDO DE TESTE - GRANDE 3 
SABORES","quantity":1,"price":0.0,"subItemsPrice":55.98,"totalPrice":55.98,"discount":0.0,"addition":0.0,"externalCode":"25","subIte
ms":[{"name":"BORDA DE CHEDDAR","quantity":1,"price":6.0,"totalPrice":6.0,"discount":0.0,"addition":0.0,"externalCode":"3000"},
{"name":"1\/3 3 QUEIJOS","quantity":1,"price":16.66,"totalPrice":16.66,"discount":0.0,"addition":0.0,"externalCode":"30"},
{"name":"1\/3 CALABRESA","quantity":1,"price":15.0,"totalPrice":16.66,"discount":0.0,"addition":1.66,"externalCode":"31"},
{"name":"1\/3 MUSSARELA","quantity":1,"price":10.0,"totalPrice":16.66,"discount":0.0,"addition":6.66,"externalCode":"32"}]}]

aqui uma simples leitura do array json em um botao do delphi

procedure TfrmPrincipal.BitBtn6Click(Sender: TObject);
var
  LJsonArr   : TJSONArray;
  i: integer;
  ObjetoJsonLinha      : TJSONObject;

begin
      LJsonArr    := TJSONObject.ParseJSONValue(TEncoding.ASCII.GetBytes(mJson.Lines.Text),0) as TJSONArray;
      if (LJsonArr.Count > 0) then
      for I:=0 to LJsonArr.Count  - 1 do
      begin
      ObjetoJsonLinha := LJsonArr.Items as TJSONObject;
       showmessage( objetojsonlinha.GetValue('name').value );
       showmessage( objetojsonlinha.GetValue('subItems').value );
      end;
end;

o array é carregado normalmente, quando uilizo o showmessage para mostrar o valor dentro do campo NAME a mensagem é retornada corretamente, quando utilizo o showmessage para mostrar o conteudo ou tento pegar o conteudo dentro do campo subItems ele dá erro de acesso ( erro básico do delphi ).

Testando o exemplo que me passou se eu deixo o array completo como acima ele me diz que não é um objeto válido ( pois é um array ) se eu tiro os colches e deixo como objeto json e não array eu clico no botao e ele nao processa nada ( vide imagem )

imagem.PNG.5d2a4adb120519a4b4bf7ea51242d7a1.PNG

preciso apenas ler o valor dentro do campo subitens ( que será o array json contendo os valores dos campos subitens,  name, quantity, price ( etc etc ), porém sempre que tento acessar isto no delphi com tostring, value ou qualquer outra funcão relacionada a subItems ele dá erro e não consigo resolver isto, já tentei muitos exemplos e nada...

 

 


 

Share this post


Link to post
Share on other sites
1 hora atrás, AlexBecker disse:

Testando o exemplo que me passou se eu deixo o array completo como acima ele me diz que não é um objeto válido

Isso acontece pois você colocou apenas uma parte do JSON de retorno do iFood. Como eu criei uma classe que lê todos os campos de retorno. Você precisa colocar o JSON de retorno completo. (e funciona caso um item tenha subitems e o outro item não tenha)

Ou seja, eu não preciso ficar desmembrando o JSON. Em apenas uma linha converto para um objeto (no caso a classe que eu criei - Essa classe deve contem todos os campos retornados).

Depois eu vou acessando esse objeto. Em tese é a mesma coisa que o ACBr para ler um XML. A única diferença é que o ACBr vai montando o objeto com o retorno da SEFAZ e no meu caso eu utilizo uma função do Próprio Delphi (JsonToObject) para montar o meu objeto.

Acredito que seja melhor você criar um objeto como falei na primeira resposta. Pois assim, ficará bem mais fácil pra dar manutenção depois. Além de que pra acessar os dados fica de forma simples.

Share this post


Link to post
Share on other sites
1 hora atrás, Gabriel Franciscon disse:

Isso acontece pois você colocou apenas uma parte do JSON de retorno do iFood. Como eu criei uma classe que lê todos os campos de retorno. Você precisa colocar o JSON de retorno completo. (e funciona caso um item tenha subitems e o outro item não tenha)

Ou seja, eu não preciso ficar desmembrando o JSON. Em apenas uma linha converto para um objeto (no caso a classe que eu criei - Essa classe deve contem todos os campos retornados).

Depois eu vou acessando esse objeto. Em tese é a mesma coisa que o ACBr para ler um XML. A única diferença é que o ACBr vai montando o objeto com o retorno da SEFAZ e no meu caso eu utilizo uma função do Próprio Delphi (JsonToObject) para montar o meu objeto.

Acredito que seja melhor você criar um objeto como falei na primeira resposta. Pois assim, ficará bem mais fácil pra dar manutenção depois. Além de que pra acessar os dados fica de forma simples.

Na verdade pelo array do json isto funciona corretamente mesmo separado, o erro é muito mais 'vergonhoso' do que isto... o erro é simplesmente gerado porque usei um MEMO também para retornos grandes, meu memo estava com worwrap = true, esta quebrando o json... como pode observar justamente onde tem o subItems ele está quebrando para uma próxima linha o que ocasionava o erro quando tentava pegar o valor do json subitems, nada mais que isto... acabei descobrindo por acaso pois como estava quebrando entao alterei a propriedade do mesmo para poder ver a linha toda pois meu monito é 2k podendo visualizar linhas bem longas na tela, fiz isto para encontrar um possível erro de formatação no retorno e a partir dai: 

      LJsonArr    := TJSONObject.ParseJSONValue(TEncoding.ASCII.GetBytes(ObjetoJson.GetValue('items').ToString),0) as TJSONArray;

      if (LJsonArr.Count > 0) then
      for I:=0 to LJsonArr.Count  - 1 do
      begin
        ObjetoJsonLinha := LJsonArr.Items as TJSONObject;

        vItensJson           := ObjetoJsonLinha.ToString;
        jstodataset( tbItems, vItensJson );


        //showmessage(ObjetoJsonLinha.ToString);
        try
        if objetojsonlinha.GetValue('subItems').ToString <> '' then
          jstodataset ( tbsubItems, objetojsonlinha.GetValue('subItems').ToString );

          showmessage( objetojsonlinha.GetValue('subItems').ToString );
        except

        end;
      end;

 

começou a funcionar normalmente...  deu só um pouquinho de raiva na hora rsrsrsrsrsr.

Bem, muito obrigado mesmo! suas dicas foram e tem sido de grande ajuda, utilizei exemplos teus e de vários outros programadores da web e tenho conseguido ótimos resultados com performance satisfatória!  

Edited by AlexBecker

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×
×
  • Create New...