JSON – O que é e como usar em AdvPL

Introdução

Em alguns tópicos anteriores, mencionei um formato de representação de objetos chamado JSON — acrônimo de JavaScript Object Notation. Bem, está na hora de ver um pouco mais sobre o que é isso e como funciona 😀

O que é JSON

JSON é uma forma de representação de objetos (e dados) em texto, com separadores e delimitadores. Em linhas gerais, todas as representações de dados em arquivo texto com algum tipo de estruturação usa separadores e delimitadores, como por exemplo XML e derivados … a diferença básica está na simplicidade.

A especificação do formato JSON é independente de linguagem ou plataforma, mas usa “convenções” (símbolos e terminologias) familiares aos programadores de linguagens da família “C” ( C, C++, C#, Java, JavaScript).  Ela parte da representação de uma informação qualquer (VALOR), identificada por um nome (CHAVE), no formato CHAVE : VALOR, onde o VALOR é a informação em si, que pode ser:

  • uma string  / texto
  • um número
  • um booleano ( true ou false )
  • null ( ou nulo )
  • um objeto 
  • um array (ou lista)

A sacada está nos dois últimos tipos de valores, que atuam como “agrupadores” de valores. Os tipos básicos de um valor são apenas os quatro primeiros. Um objeto contem um agrupamento de CHAVE=VALOR delimitado por chaves “{ }” e separados por vírgula, enquanto um array ou lista é delimitado por colchetes “[ ]” e pode conter qualquer um dos valores acima separados por vírgula.

Podemos representar um objeto “vazio” como  { } , um objeto com uma propriedade chamada nome com valor nulo como  { “nome”: null } , um objeto com duas propriedades , onde uma delas é um número e outra um array de números como { “p1”: 0, “p2”: [1,2,3] } e assim por diante.

Um valor string (ou texto) deve ser delimitado por aspas duplas, e pode conter qualquer sequência de bytes dentro dela, exceto uma aspa dupla (“) ou uma barra inversa ( \ ), que caso façam parte do conteúdo, podem ser representados respectivamente como  \” \\ . Um nome que representa uma chave deve ser uma string, um número pode ser representado com parte decimal separada por “.” ponto e até mesmo usar notação científica, e os valores true, false e null são representados exatamente assim como está escrito. Um array é delimitado por colchetes, pode ter nenhum, um ou mais valores (chamados de “elementos”), onde cada elemento pode ser um valor de qualquer tipo, inclusive objeto ou array — arrays encadeados ou multidimensionais.

Onde esse formato é usado ?

A especificação JSON é usada em APIs de troca de dados e processamento (SOA), como por exemplo os WebServices REST, o banco de dados MongoDB foi feito para armazenar, recuperar e manipular objetos JSON, arquivos de configuração de diversas aplicações e projetos, o céu é o limite — pergunte pro Google, JSON é usado em uma ampla gama de implementações, ferramentas e plataformas.

Dá para usar JSON para uma infinidade de tarefas, mas lembre-se que ele não é “bala de prata”, para cada tamanho de problema e necessidade, existem alternativas que se encaixam perfeitamente ou atendem melhor… Tudo depende do tamanho e especificação da sua necessidade, e como você a implementa.

JSON no AdvPL

Para ler, manipular e gerar uma string com JSON, a linguagem AdvPL fornece uma classe chamada JsonObject — vide link da TDN no final do post — e uma sintaxe no AdvPL que permite um enumerador de array do tipo “C” Caractere no AdvPL. Vamos ver como utilizá-la inicialmente para obter o conteúdo de um arquivo JSON, usando o arquivo de exemplo abaixo — podemos salvá-lo como “exemplo1.json”:

{ 
  "Id": 123456,
  "Nome": "Joanilson Wesley",
  "Alergias": [ "Aspirina","Paracetamol" ],
  "Dependentes": false,
  "Outros": {
    "Sangue": "A+",
    "Eleitor": true,
    "Reservista": true
  }
}

Exemplo de Leitura em AdvPL

Este arquivo contém um objeto, com as propriedades “Id” (numérica, valor 123456), “Nome” (string, valor “Joanilson Wesley”), “Alergias” (array de strings, contendo os valores “Aspirina” e “Paracetamol”), e a propriedade “Dependentes” com valor false, e a propriedade Outros contém um novo objeto com três propriedades. Partindo da premissa que este é o formato conhecido e padrão deste objeto, para obter os valores das propriedades deste JSON em AdvPL, podemos fazer o seguinte:

User Function Exemplo1()
Local cFile := '\teste\exemplo1.json'
Local cJsonStr,oJson
Local nId, cNome, lDepend, aAlerg
Local oOutros, cTipoS, lEleitor, lReserv

// Le a string JSON do arquivo do disco 
cJsonStr := readfile(cFile)

// Cria o objeto JSON e popula ele a partir da string
oJson := JSonObject():New()
cErr  := oJSon:fromJson(cJsonStr)

If !empty(cErr)
  MsgStop(cErr,"JSON PARSE ERROR")
  Return
Endif

// Agora vamos ler as propriedades com GetJSonObject()

nId := oJson:GetJSonObject('Id')
cNome := oJson:GetJSonObject('Nome')
lDepend := oJson:GetJSonObject('Dependentes')

// Aqui será retornado um array de strings
aAlerg := oJson:GetJSonObject('Alergias')
cAlerg1 := aAlerg[1]
cAlerg2 := aAlerg[2]

// Objeto dentro de uma propriedade
oOutros := oJson:GetJSonObject('Outros')

cTipoS := oOutros:GetJSonObject('Sangue')
lEleitor := oOutros:GetJSonObject('Eleitor')
lReserv := oOutros:GetJSonObject('Reservista')

// Descarta o objeto 
FreeObj(oJson)

Return

STATIC Function ReadFile(cFile)
Local cBuffer := ''
Local nH , nTam
nH := Fopen(cFile)
IF nH != -1
	nTam := fSeek(nH,0,2)
	fSeek(nH,0)
	cBuffer := space(nTam)
	fRead(nH,@cBuffer,nTam)
	fClose(nH)
Else
	MsgStop("Falha na abertura do arquivo ["+cFile+"]","FERROR "+cValToChar(Ferror()))
Endif
Return cBuffer

Comportamentos e pontos de atenção

  • Embora na orientação a objetos do AdvPL, uma instância de uma classe possui o tipo “O” (Object), para o JSON o tipo do objeto retornado é “J” — JSON Object— verificamos o tipo do conteúdo de uma variável AdvPL usando a função ValType().
  • O método GetJsonObject() recupera o conteúdo de qualquer propriedade, já realizando a “conversão” da informação para um tipo AdvPL. Por exemplo, uma propriedade contendo uma string retorna uma variável do tipo “C” (Caractere) do AdvPL, um valor booleano retorna .T. ou .F., um valor numérico retorna um tipo “N” numérico em AdvPL, um Array retorna um “A” Array mesmo, e uma propriedade retorna uma instância de objeto JSON.
  • Os nomes das chaves informadas o método GetJsonObject() são “case sensitive” (letras maiúsculas são diferentes de minúsculas). Caso seja solicitado um valor cujo identificador não exista no JSON, o método retorna NIL.

Sintaxe mais amigável

Um Objeto JSON em AdvPL permite acessar as suas propriedades de uma forma mais “elegante”, veja no exemplo abaixo:

// Ao invés de
nId := oJson:GetJsonObject('Id')
// Você pode utilizar
nId := oJson['Id']

// As recuperações dos valores do exemplo acima 
// poderiam ser escritas assim:

nId := oJson['Id']
cNome := oJson['Nome']
lDepend := oJson['Dependentes']
cAlerg1 := oJson['Alergias'][1]
cAlerg2 := oJson['Alergias'][2]
cTipoS := oJson['Outros']['Sangue']
lEleitor := oJson['Outros']['Eleitor']
lReserv := oJson['Outros']['Reservista']

Esta sintaxe é mais “clean“, e permite encadeamentos de forma simples. Mas lembre-se que um acesso a uma propriedade que não existe retorna NIL, e se você não verifica o retorno e usa um encadeamento, o próximo nível de encadeamento fatalmente vai apresentar um erro de execução. Se o array de alergias somente tiver um elemento, acessar o elemento vai encerrar o processo com um erro Array out of bounds. O encadeamento oJson[‘Outros’][‘Reservista’] será finalizado com um erro “Variable is not a array” caso a propriedade ‘Outros’ não exista.

Demais métodos

Um método interessante é o GetNames() — ele retorna um array de strings em Advpl, contendo o nome das propriedades de um objeto JSON. Com ele você pode enumerar e acessar todas as propriedades de um objeto JSON mesmo sem saber o seu nome.

Considerações importantes

Um ponto importante a ser lembrado é o seguinte: O formato JSON como um objeto sempre parte da representação de um OBJETO — começa com “{” abertura de chaves, deve ter pelo menos uma propriedade e um valor, e encerra com “}”. Você pode ter um arquivo em disco com múltiplas linhas, onde cada linha tem um objeto JSON — por exemplo representando um registro de uma tabela — mas isso não é um ARRAY de Objetos — são objetos independentes, um por linha. Ao ler um arquivo formatado dessa forma, cada linha deve ser tratada como um objeto independente, e não simplesmente ler o arquivo inteiro e jogar para o parser. O Parser vai entender e avaliar apenas a primeira linha. Ele somente será um array de objetos se houver um objeto JSON de nível superior, com uma propriedade declarada com um array, onde as demais linhas serão elementos desse array, devidamente separados por vírgula.

Conclusão

É mais simples do que a gente imagina antes de conhecer, e muito prático para uso. Não é a toa que ele é muito utilizado 😀 Para continuar lendo sobre esse assunto, acesse o próximo post — JSON – O que é e como usar em AdvPL – Parte 02 — onde veremos como alterar e acrescentar propriedades no objeto JSON.

Agradeço novamente a audiência, espero que este conhecimento lhe seja útil, e lhes desejo TERABYTES DE SUCESSO !!!

Referências

53 respostas em “JSON – O que é e como usar em AdvPL

  1. Pingback: JSON – O que é e como usar em AdvPL – Parte 02 | Tudo em AdvPL

  2. Pingback: MongoDB em AdvPL – Prova de Conceito | Tudo em AdvPL

  3. Julio, excelente post, utilizei basicamente a classe que utilizou para recuperar cotação de moedas, porém não consigo extrair nem as chaves e nem o conteúdo na na ‘value’, este é o Json:
    {“@odata.context”:”https://was-p.bcnet.bcb.gov.br/olinda/servico/PTAX/versao/v1/odata$metadata#_CotacaoMoedaDia”,”value”:[{“paridadeCompra”:1.00000,”paridadeVenda”:1.00000,”cotacaoCompra”:4.24790,”cotacaoVenda”:4.24850,”dataHoraCotacao”:”2019-11-27 10:11:20.069″,”tipoBoletim”:”Abertura”},{“paridadeCompra”:1.00000,”paridadeVenda”:1.00000,”cotacaoCompra”:4.26000,”cotacaoVenda”:4.26060,”dataHoraCotacao”:”2019-11-27 11:05:17.297″,”tipoBoletim”:”Intermediário”},{“paridadeCompra”:1.00000,”paridadeVenda”:1.00000,”cotacaoCompra”:4.26660,”cotacaoVenda”:4.26720,”dataHoraCotacao”:”2019-11-27 12:11:18.316″,”tipoBoletim”:”Intermediário”},{“paridadeCompra”:1.00000,”paridadeVenda”:1.00000,”cotacaoCompra”:4.26400,”cotacaoVenda”:4.26460,”dataHoraCotacao”:”2019-11-27 13:09:31.444″,”tipoBoletim”:”Intermediário”},{“paridadeCompra”:1.00000,”paridadeVenda”:1.00000,”cotacaoCompra”:4.25960,”cotacaoVenda”:4.26020,”dataHoraCotacao”:”2019-11-27 13:09:31.449″,”tipoBoletim”:”Fechamento PTAX”}]}

    Curtido por 1 pessoa

    • Opa, beleza ? Obrigado …rs… Então, a propriedade ‘value’ é um array de objetos JSON. Você acessa ele em AdvPL como um array normal, veja o exemplo abaixo — montado para ler as cotações :

      USER Function JMoedas()
      Local cStr, oJson, nI
      Local aValues

      cStr := memoread(‘\moedas.txt’)
      oJson := JSonObject():New()
      oJson:fromJson(cStr)

      aValues := oJson[‘value’]

      For nI := 1 to len(aValues)
      conout(‘Cotacao ‘+cValToChar(nI)+”/”+cValToChar(len(aValues)))
      conout(aValues[nI][‘paridadeCompra’])
      conout(aValues[nI][‘paridadeVenda’])
      conout(aValues[nI][‘cotacaoCompra’])
      conout(aValues[nI][‘cotacaoVenda’])
      conout(aValues[nI][‘tipoBoletim’])
      Next

      Return

      Curtir

  4. Pingback: JSON – O que é e como usar em AdvPL – Parte 03 – zJsonKit | Tudo em AdvPL

  5. Olá!

    Uma dúvida… As funções de retorno das propriedades do objeto JSON são case sensitive?

    Num teste quando utilizo oJson:GetJsonText(“zipCode”) é retornado, mas “ZIPCODE” ou “zipcode” não. Mas, em outra propriedade “ADDRESS” ou “address” é retornada; já “Address” a propriedade do objeto não é retornada.

    Curtido por 1 pessoa

    • JSON é tratado pelo AdvPL como “Case Sensitive”, logo a recuperação do conteúdo de uma propriedade em AdvPL deve informar o nome da propriedade exatamente como foi especificado no JSON, tanto faz se a abordagem usada for oJson:GEtJsonText(“propriedade”) ou oJson[“propriedade”] 😀

      Curtir

      • Júlio deixe-me compreender melhor… (gerou outra dúvida)… O objeto JSON AdvPL trata o nome da propriedade como “Case Sensitive”?! Por exemplo oJson:GetJsonText(“propriedade”) é o mesmo que oJson:GetJsonText(“Propriedade”) e o mesmo que oJson:GetJsonText(“PROPRIEDADE”)? O nome da propriedade do objeto, “propriedade”, pode misturar maiúsculas e minúsculas?

        Outro ponto… ao final usou como exemplo oJson:GEtJsonText(“propriedade”) e oJson[“propriedade”], então podemos utilizar o método “GetJsonText” do objeto ou utilizar o objeto JSON como um array passando o termo/propriedade como índice?

        Curtido por 1 pessoa

    • Opa, vejamos… As propriedades escritas em um JSON podem sim misturar letras maiúsculas e minúsculas, sem problemas. Mas como o tratamento das propriedades é “case sensitive” significa que letras maiúsculas e minúsculas são tratadas de modo diferente. Se o seu Objeto JSON tem uma propriedade chamada “Nome”, com N maiúsculo e o resto minúsculo, você somente vai conseguir recuperar ela no AdvPL usando a string “Nome”, exatamente igual — por exemplo cNome := oJson[‘Nome’].

      E, exclusivamente para um Objeto JSON em AdvPL, você pode usar a sintaxe acima, como um “array”, informando no índice a string com o nome da propriedade, como se fosse o índice do array. Inclusive, essa forma de acesso oJson[‘Propriedade’] é a mais indicada, pois com ela eu posso recuperar diretamente já convertido para AdvPL strings, números, objetos e arrays como propriedade 😀

      Curtir

      • Então de temos no objeto JSON a “propriedade” com valor numérico ao utilizar o método do objeto AdvPL, oJson:GetJsonText(“propriedade”), esta “propriedade” do objeto JSON é retornada como string?!

        // Objeto JSON {“propValor”:”123.45″,propValorTotal:456.78} – Objecto Json AdvPL oJson
        cPropValor := oJson:GetJsonText(“propValor”) // RETORNA VALOR STRING
        nPropVlTot := oJson:GetJsonText(“propValorTotal”) // RETORNA VALOR STRING (?! mesmo sendo numérico no objeto JSON)
        _cPrpValor := oJson[“propValor”] // RETORNA VALOR STRING
        _nPrpVlTot := oJson[“propValorTotal”] // RETORNA VALOR NUMERICO (?!)

        Curtido por 1 pessoa

      • Sim, é isso mesmo. O método GetJsonTEXT() serve justamente para isso, retornar o conteúdo de uma propriedade de tipo simples ( não array ou objeto ) como string, sem tratamento. Se um determinado valor está representado como um tipo numérico no JSON, e eu quero ele já “tratado” e convertido para o tipo numérico do AdvPL, eu uso a recuperação direta da propriedade como um enumerador de array ( objeto[‘propriedade’] ) ou o método GetJSonObject(‘propriedade’) 😀 –> Ambas as formas recuperam um dado já convertido e tratado, inclusive propriedades do tipo objeto e arrays 😉

        Curtir

  6. Opa Júlio, tudo bem?
    já consegui fazer uma API básica graças a esses dois Posts seus, agora estou tentando resolver um problema e não estou conseguindo, queria montar um array com uma requisição assim:

    {
    “telefones”: [
    {
    “tipo”: “1”,
    “ddd”: “11”,
    “numero”: “999999999”
    },
    {
    “tipo”: “5”,
    “ddd”: “65”,
    “numero”: “910102020”
    }
    ]
    }

    Cada chave dessa com o tipo, ddd e número queria colocar em uma posição do array para ao final ele ficar assim

    cValToChar(array[2][3]) // resultado 910102020

    Curtido por 1 pessoa

    • Opa, beleza ? Então, a representação em texto acima é de um Objeto JSON, certo ? Em AdvPL eu posso montar ele como JSON com array de objetos JSON, ou como um array multi-dimensional … Se o array que você deseja deve ser recuperado usando os indices/posições, é um array multi-dimensional… onde um elemento contém outro array… algo assim:

      aFones := {}
      aadd(aFones,{“1″,”11″,”999999999”} )
      aadd(aFones,{“5″,”65″,”910102020”} )
      conout(aFones[2][3]) // retorna “910102020”

      😀

      Curtir

    • Agora, se a sua dúvida é como “criar” a representação desse JSON em AdvPL, ficaria assim:

      oJson := tJsonObject():New()
      oJson[‘telefones’] := {}
      oPhone := tJsonObject():New()
      oPhone[‘tipo’] := ‘1’
      oPhone[‘ddd’] := ’11’
      oPhone[‘numero’] := ‘999999999’
      aadd(oJson[‘telefones’],oPhone)
      oPhone := tJsonObject():New()
      oPhone[‘tipo’] := ‘5’
      oPhone[‘ddd’] := ’65’
      oPhone[‘numero’] := ‘910102020’
      aadd(oJson[‘telefones’],oPhone)

      Porem, neste caso você acessaria o segundo numero de telefone assim:
      conout( oJson[‘telefones’][2][‘numero’])

      😀

      Curtir

  7. Opa Júlio, obrigado por responder, fiquei meio confuso pra falar a verdade rs
    Acho que agora a gente chega lá, sim essa é a representação de um Objeto JSON, vou enviar ela pelo o Postman depois, nesse segundo exemplo até daria certo, só que esses telefones são vari avies, pode ser 1, 2, 3 ou até 4, então precisaria de uma estrutura de repetição que percorra todas as chaves do objeto telefone.

    E ai final esse seria o resultado, um array de arrays, onde cada um é uma chave do objeto telefone.

    aPhones:{}
    aPhones:= { {“1”, “11”, “999999999”}, {“5”, “65”, “910102020”}}

    Curtido por 1 pessoa

    • Certo … Bem, se eu entender melhor a sua necessidade, eu acho que consigo dar uma explicação mais “aderente” a sua dificuldade. O tipo Array em AdvPL pode ter um ou mais elementos, e cada elemento do array pode ser de um tipo ( caractere, numérico, data, nulo, array ou objeto ). Um objeto do tipo JSON em AdvPL possui uma sintaxe de acesso parecida com um Array em AdvPL, a diferença é que, se o elemento do array é um objeto do tipo JSON ( Tipo “J” em AdvPL ), eu posso acessar uma propriedade desse objeto usando entre os colchetes a string com o nome da propriedade, enquanto um array em AdvPL aceita apenas números como identificador de elemento. E, a propriedade de um objeto JSON pode ter como conteúdo um Array de tipos suportados pelo JSON ( caractere, numérico, array ou outro objeto JSON). ME conte melhor o que você quer fazer, que eu posso tentar te mostrar um caminho para chegar lá 😀

      Curtir

      • Bem estou precisando criar uma API, que insira novos números de telefones na lista de telefones de um contato de forma que fique um histórico. Esses contatos ficam na tabela SU5, mas essa lista de telefones fica na tabela AGB, procurei algumas soluções como:

        1 – API DA TOTVS: {host}:{porta}/rest/CRMMCONTACTS/{contact_id} essa api recebe um json praticamente igual esse que eu coloquei lá no inicio. No entanto, essa api tem alguns bugs, ela insere todos os número de telefones como sendo padrão, mesmo você informando “default”: “2”, e não atualiza para “default”: “2”, os números que foram informados anteriormente, se você for editar este contato vai aparecer uma mensagem de erro, informando que existe mais de um número de telefone do mesmo tipo marcado como padrão.

        E o outro bug, é que ele limpa os campos da tabela SU5 que não foram informados na requisição, por exemplo, digamos que este contato tem todos os números cadastradas lá U5_FONE, U5_CELULAR, U5_FCOM1 E U5_FCOM2 e na requisição eu informe apenas o celular, este novo número de celular é adicionado na tabela AGB, seu valor é atualizado em U5_CELULAR, mas os campos U5_FONE, U5_FCOM1 E U5_FCOM2 são limpos.

        2 – Usar uma rotina automática para atualizar dados de um contato, foi aí que eu encontrei a TMKA070 https://tdn.totvs.com/pages/releaseview.action?pageId=6787712 fiz vários testes e ela não insere novos números de telefones quando eu coloco a opção 4, que é para atualizar o contato.

        Com isso, resolvi procurar uma solução de tentar fazer isso na unha, então o que eu estou fazendo, usando
        dbSeek e procurando o contato pelo o CPF depois disso eu estou dando um update na tabela AGB E alterando o valor do campo AGB_PADRAO de 1 para 2.

        cComando := ” UPDATE ” + RetSQLname(“AGB”) + ” SET AGB_PADRAO = ‘2’ ”
        cComando += ” WHERE AGB_FILIAL = ‘” + xFilial(“AGB”) + “‘”
        cComando += ” AND AGB_CODENT = ‘” + SU5->U5_CODCONT + “‘”
        cComando += ” AND AGB_TIPO = ‘1’ ”
        cComando += ” AND D_E_L_E_T_ ‘*’ ”

        nQueryRet:= TcSqlExec(cComando)
        TcRefresh(RetSQLname(“AGB”))

        Nesse trecho de código eu gostaria de fazer uma verificação para ele alterar somente as linhas que o campo AGB_TIPO correspondesse ao que vem da requisição, pois não faz sentido caso eu estivesse querendo inserir somente o número de celular e alterasse para AGB_PADRAO = ‘2’ os outros tipo

        Agora eu faria a inserção dos números de telefones que vem da requisição, e setando elas como padrão, nessa etapa também teria uma estrutura de repetição, pois na requisição pode ter o número de celular, residencial, comercial…

        DbSelectArea(“AGB”)
        cCodigo := GetSxeNum(“AGB”,”AGB_CODIGO”)
        ConfirmSX8()
        RecLock(‘AGB’, .t.)
        AGB->AGB_FILIAL:= SU5->U5_FILIAL
        AGB->AGB_CODIGO := cCodigo
        AGB->AGB_ENTIDA := “SU5”
        AGB->AGB_CODENT := SU5->U5_CODCONT
        AGB->AGB_TIPO := “5” // Esse tipo aqui tem que ser substituído pelo que vem da requisição
        AGB->AGB_PADRAO := “1”
        AGB->AGB_DDD := “87”
        AGB->AGB_TELEFO := “911223344”
        AGB->AGB_COMP := “Incluso em: ” + cValToChar(Date()) + ” às ” + cTime
        AGB->(MsUnlock())

        E por fim, rodava
        MSExecAuto({|x,y|TMKA070(x,y)},aContato,4)

        Essa parte eu testei, os números que são inseridos na tabela AGB e se forem marcados como padrão, ao atualizar esses são replicados na tabela SU5.

        Uffa, eu não sei se vai entender, espero que sim, me esforcei tentar explicar o máximo possível 😀

        Curtido por 1 pessoa

      • Hummm.. entendi … Bem, se a sua API vai receber um JSON, e vai vir no formato que você colocou no post, você faz um loop da propriedade do objeto que recebe o array de telefones de forma assim:

        nTam := len( oJson[‘telefones’] )
        For nI := 1 to nTam
        oTelefone := oJson[‘telefones’][nI]
        cTipo := oTelefone[‘tipo’]
        cDDD :=oTelefone[‘ddd’]
        // aqui o resto da lógica de atualização …
        Next

        Quanto a demais funcionalidades, MSExecAuto() e afins, eu acho interessante você dar uma pesquisada nos fóruns de desenvolvimento AdvPL — Na barra lateral do BLOG tem alguns Links, o “Helpfácil” tem bastante conteúdo, e você pode fazer um post pedindo ajuda ou informações 😀

        Abraços e boa sorte 😀

        Curtido por 1 pessoa

  8. Oi Júlio, queria te agradecer, graças a você eu consegui resolver meu problema, ta funcionando perfeitamente, só não sei se estou seguindo as melhores práticas, mas isso depois eu vejo com alguém que tenha mais experiência.

    Abraços e até a próxima

    Curtido por 1 pessoa

  9. Maaaaaano do céu!!! Resumiu tudo o que eu precisava saber! Estou refatorando alguns códigos para adaptar por conta do FWJsonDeserialize ter sido descontinuado.

    Parabéns pelo post

    Agora vou por mãos a obra e te perturbar aqui caso tenha dúvida kkkkkkkkkkk…

    Curtido por 1 pessoa

  10. Chefe, boa noite! Veja se estou fazendo algo errado:

    Preciso pegar o conteúdo de um JSON em um WebService REST mas não estou conseguindo.
    Vejam o trecho do código.

    //recebo o conteúdo JSON
    Local cJsonStr := Self:GetContent()

    // Cria o objeto JSON e popula ele a partir da string
    oJson := JSonObject():New()
    cErr := oJSon:fromJson(cJsonStr)

    //teoricamente, de acordo com a documentação, agora eu poderia pegar o conteúdo dos campos, por exemplo:
    cConteudo := oJson:GetJSonObject(‘Nome_campo’)

    Mas isto não acontece, o conteúdo sempre retorna nulo

    Já conferi, e o JSON está vindo normal e o objeto oJson fica como tipo “J”

    O Json que estou recebendo é exatamente este:

    {
    “MATFAM”: “00010002000002”,
    “TIPPAG_FAM”: “03”,
    “TITULOS”: [
    {
    “CHAVE_TIT”: “01PLS000000022 DP “,
    “COMPETENCIA”: “05/2020”,
    “DESC_FORMREC”: “BOLETO REGISTRADO “,
    “FORMREC”: “03”,
    “RECNO_TIT”: 20,
    “VENCIMENTO”: “04/05/2020”
    },
    {
    “CHAVE_TIT”: “01PLS000000024 DP “,
    “COMPETENCIA”: “06/2020”,
    “DESC_FORMREC”: “BOLETO REGISTRADO “,
    “FORMREC”: “03”,
    “RECNO_TIT”: 22,
    “VENCIMENTO”: “01/06/2020”
    },
    {
    “CHAVE_TIT”: “01PLS000000026 DP “,
    “COMPETENCIA”: “07/2020”,
    “DESC_FORMREC”: “BOLETO REGISTRADO “,
    “FORMREC”: “03”,
    “RECNO_TIT”: 24,
    “VENCIMENTO”: “01/07/2020”
    }
    ]
    }

    Socooooorro… kkkkkkk

    Curtido por 1 pessoa

  11. Boa tarde Júlio, poderia me dar um help? estou fazendo meu primeiro fonte usando json e estou cm bastante dúvida em como montar o seguinte json em ADVPL.

    “customFieldValues”: [
    {
    “value”: “00000101”,
    “customField”: {
    “id”: 157827
    }
    },
    {
    “value”: “Fantasia teste”,
    “customField”: {
    “id”: 157398
    }
    },
    {
    “value”: “534560100001”,
    “customField”: {
    “id”: 157397
    }
    },
    {
    “value”: “Física”,
    “customField”: {
    “id”: 158000
    }
    }
    ]

    Curtido por 1 pessoa

    • Olá Diego, tudo bom ?
      O interessante desse JSON que você quer montar, é que ele não inicia com um “{“, ele representa uma propriedade de um Json, cujo conteúdo são arrays de objetos. Em AdvPL isso ficaria assim :

      #include “protheus.ch”

      User Function MyJson()
      Local oJson := JsonObject():New()

      // Cria a propriedade como um array
      oJson[‘customFieldValues’] := {}

      // Cria um elemento e acrescenta no array
      oObj := JsonObject():New()
      oObj[‘value’] := “00000101”
      oObj[‘customField’] := JsonObject():New()
      oObj[‘customField’][‘id’] := 157827
      aadd(oJson[‘customFieldValues’],oObj)

      // Cria um elemento e acrescenta no array
      oObj := JsonObject():New()
      oObj[‘value’] := “Fantasia teste”
      oObj[‘customField’] := JsonObject():New()
      oObj[‘customField’][‘id’] := 157398
      aadd(oJson[‘customFieldValues’],oObj)

      // Cria um elemento e acrescenta no array
      oObj := JsonObject():New()
      oObj[‘value’] := “534560100001”
      oObj[‘customField’] := JsonObject():New()
      oObj[‘customField’][‘id’] := 157397
      aadd(oJson[‘customFieldValues’],oObj)

      // Cria um elemento e acrescenta no array
      oObj := JsonObject():New()
      oObj[‘value’] := “Física”
      oObj[‘customField’] := JsonObject():New()
      oObj[‘customField’][‘id’] := 158000
      aadd(oJson[‘customFieldValues’],oObj)

      // mostra o JSON gerado no console
      conout(oJson:ToJSON())

      Return

      O resultado mostrado no console deve ser o que você espera … só que por ser um objeto JSON, o bloco inteiro começa com uma abertura de chave “{” e termina com o fechamento de chave “}”

      {“customFieldValues”:[{“customField”:{“id”:157827},”value”:”00000101″},{“customField”:{“id”:157398},”value”:”Fantasia teste”},{“customField”:{“id”:157397},”value”:”534560100001″},{“customField”:{“id”:158000},”value”:”Física”}]}

      A ordem das propriedades dentro do objeto ao representá-lo em texto podem não refletir a ordem de criação deles, mas isso não é relevante para parsear o objeto.

      Curtir

  12. Prezado,

    Por favor fiz o fonte abaixo e só dá PARSE ERROR. Já testei o json e é válido:

    //——————————————————–
    #include ‘protheus.ch’

    User function api001
    local oJson, cErr, cJsonStr, cRFnome

    cJsonStr := ‘{“nome”,”roger”}’
    oJson := JSonObject():New()
    cErr := oJSon:fromJson(cJsonStr)

    If !empty(cErr)
    MsgStop(cErr,”JSON PARSE ERROR”)
    Return
    Endif

    cRFnome := oJson:GetJSonObject(‘nome’)
    msginfo(cRFnome,”Ok”)
    return nil
    //——————————————————–

    Obs: A ideia na verdade é que a variável cJsonStr, recebesse seu conteudo via URL:

    cUrl := “http://viacep.com.br/ws/27257090/json”
    cJsonStr := HttpGet(cUrl, cGetParams, nTimeOut, aHeadStr, @cHeaderGet)

    Favor analisar. Agradeço sua atenção.
    Muito obrigado!

    Curtido por 1 pessoa

    • Opa, beleza ? Vejamos …

      No exemplo de código / JSON , o separador entre o identificador ( “nome” ) e o valor ( “roger” ) deve ser “:” ( dois pontos ), e não “,” (vírgula). Experimente trocar para ‘{“nome”:”roger”}’ 😀

      Quanto a necessidade de recuperar o JSON por HTTP, está certinho, apenas neste caso nao precisa dos parametros opcionais na função httget … veja o exemplo abaixo — testado, funcionando — faltam apenas os tratamentos de erro …

      User Function ViaCEP()

      cJson := Httpget(“http://viacep.com.br/ws/27257090/json”)
      oJson := JSonObject():New()
      oJson:FromJson(cJson)

      conout(“cep ………. “+oJson[‘cep’])
      conout(“logradouro … “+oJson[‘logradouro’])
      conout(“complemento .. “+oJson[‘complemento’])
      conout(“bairro ……. “+oJson[‘bairro’])
      conout(“localidade … “+oJson[‘localidade’])
      conout(“uf ……….. “+oJson[‘uf’])
      conout(“unidade …… “+oJson[‘unidade’])
      conout(“ibge ……… “+oJson[‘ibge’])
      conout(“gia ………. “+oJson[‘gia’])

      Return

      Abraços 😀

      Curtido por 1 pessoa

  13. Estou com uma situação, que tenho que gravar o conteúdo de um HTTPSGet que o retorno é um Json no seguinte formato:
    {
    “total”:40,
    “limit”:5000,
    “skip”:0,
    “data”:[
    {
    “id”:”84796″,
    “documentNumber”:”324010″,
    “personId”:”52325″,
    “agreementName”:”GERCRED6″,
    “agreementDescription”:”GERDAU”,
    “issuerDocument”:”10452827000190″,
    “customerCode”:”100262132″,
    “customerName”:”ALESSANDRA BODNER ROSSI ME”,
    “invoiceId”:”65880″,
    “transactionNumber”:”55353 1″,
    “billingDate”:”2019-07-10T00:00:00.000Z”,
    “operationDays”:”35″,
    “paidAmount”:”208,29″,
    “dueDate”:”2019-08-14T00:00:00.000Z”,
    “paymentTransferValue”:”203,62″,
    “borderauxDocument”:”11828″,
    “movimentDate”:”2019-07-10T00:00:00.000Z”,
    “transferDate”:”2019-07-12T00:00:00.000Z”,
    “transferValue”:”4233310,54″,
    “indexValue”:”1,92″,
    “paymentDate”:”2019-08-16T00:00:00.000Z”,
    “bank”:”001/3400″,
    “bankAccount”:”205359″,
    “bankAccountDigit”:”4″,
    “ticketNumber”:”006001393139″,
    “liquidationStatus”:null,
    “originalDueDate”:null,
    “discount”:null,
    “sellerName”:”GERCRED56″
    },
    {
    “id”:”84797″,
    “documentNumber”:”324011″,
    “personId”:”52325″,
    “agreementName”:”GERCRED6″,
    “agreementDescription”:”GERDAU”,
    “issuerDocument”:”10452827000190″,
    “customerCode”:”100262132″,
    “customerName”:”ALESSANDRA BODNER ROSSI ME”,
    “invoiceId”:”65880″,
    “transactionNumber”:”55353 1″,
    “billingDate”:”2019-07-10T00:00:00.000Z”,
    “operationDays”:”42″,
    “paidAmount”:”208,28″,
    “dueDate”:”2019-08-21T00:00:00.000Z”,
    “paymentTransferValue”:”202,68″,
    “borderauxDocument”:”11828″,
    “movimentDate”:”2019-07-10T00:00:00.000Z”,
    “transferDate”:”2019-07-12T00:00:00.000Z”,
    “transferValue”:”4233310,54″,
    “indexValue”:”1,92″,
    “paymentDate”:”2019-08-21T00:00:00.000Z”,
    “bank”:”001/3400″,
    “bankAccount”:”205359″,
    “bankAccountDigit”:”4″,
    “ticketNumber”:”006001393147″,
    “liquidationStatus”:null,
    “originalDueDate”:null,
    “discount”:null,
    “sellerName”:”GERCRED56″
    }
    ]
    }

    A variável sGetRet está preenchida certinha com esse retorno.

    Estou tentando ler e gravar da seguinte forma abaixo:
    oJsonRet := JSonObject():New()
    cErr := oJsonRet:fromJson(sGetRet)

    If !empty(cErr)
    MsgStop(cErr,”JSON PARSE ERROR”)
    Return
    Endif

    aData := oJsonRet:GetJSonObject(‘data’)

    For i := 1 to Len(aData)

    oJData := JSonObject():New()
    oJData:fromJson(aData[i])

    cQuery := “”
    cQuery += “INSERT INTO PAYMENTREC (”
    cQuery += “ID,”
    cQuery += “documentNumber,”
    cQuery += “personId,”
    cQuery += “agreementName,”
    cQuery += “agreementDescription,”
    cQuery += “issuerDocument,”
    cQuery += “customerCode,”
    cQuery += “customerName,”
    cQuery += “invoiceId,”
    cQuery += “transactionNumber,”
    cQuery += “billingDate,”
    cQuery += “operationDays,”
    cQuery += “paidAmount,”
    cQuery += “dueDate,”
    cQuery += “paymentTransferValue,”
    cQuery += “borderauxDocument,”
    cQuery += “movimentDate,”
    cQuery += “transferDate,”
    cQuery += “transferValue,”
    cQuery += “indexValue,”
    cQuery += “paymentDate,”
    cQuery += “bank,”
    cQuery += “bankAccount,”
    cQuery += “bankAccountDigit,”
    cQuery += “ticketNumber,”
    cQuery += “liquidationStatus,”
    cQuery += “originalDueDate,”
    cQuery += “discount,”
    cQuery += “sellerName”
    cQuery += “) VALUES (”
    cQuery += “‘”+oJData:GetJSonObject(‘ID’)+”‘,”
    cQuery += “‘”+oJData:GetJSonObject(‘documentNumber’)+”‘,”
    cQuery += “‘”+oJData:GetJSonObject(‘personId’)+”‘,”
    cQuery += “‘”+oJData:GetJSonObject(‘agreementName’)+”‘,”
    cQuery += “‘”+oJData:GetJSonObject(‘agreementDescription’)+”‘,”
    cQuery += “‘”+oJData:GetJSonObject(‘issuerDocument’)+”‘,”
    cQuery += “‘”+oJData:GetJSonObject(‘customerCode’)+”‘,”
    cQuery += “‘”+oJData:GetJSonObject(‘customerName’)+”‘,”
    cQuery += “‘”+oJData:GetJSonObject(‘invoiceId’)+”‘,”
    cQuery += “‘”+oJData:GetJSonObject(‘transactionNumber’)+”‘,”
    cQuery += “TO_TIMESTAMP(substr(replace(‘”+oJData:GetJSonObject(‘billingDate’)+”‘,’T’,’ ‘),1, 19), ‘RRRR-MM-DD HH24:MI:SS.FF’),”
    cQuery += cValToChar(oJData:GetJSonObject(‘operationDays’))+”,”
    cQuery += cValToChar(oJData:GetJSonObject(‘paidAmount’))+”,”
    cQuery += “TO_TIMESTAMP(substr(replace(‘”+oJData:GetJSonObject(‘dueDate’)+”‘,’T’,’ ‘),1, 19), ‘RRRR-MM-DD HH24:MI:SS.FF’),”
    cQuery += cValToChar(oJData:GetJSonObject(‘paymentTransferValue’))+”,”
    cQuery += “‘”+oJData:GetJSonObject(‘borderauxDocument’)+”‘,”
    cQuery += “TO_TIMESTAMP(substr(replace(‘”+oJData:GetJSonObject(‘movimentDate’)+”‘,’T’,’ ‘),1, 19), ‘RRRR-MM-DD HH24:MI:SS.FF’),”
    cQuery += “TO_TIMESTAMP(substr(replace(‘”+oJData:GetJSonObject(‘transferDate’)+”‘,’T’,’ ‘),1, 19), ‘RRRR-MM-DD HH24:MI:SS.FF’),”
    cQuery += cValToChar(oJData:GetJSonObject(‘transferValue’))+”,”
    cQuery += cValToChar(oJData:GetJSonObject(‘indexValue’))+”,”
    cQuery += “TO_TIMESTAMP(substr(replace(‘”+oJData:GetJSonObject(‘paymentDate’)+”‘,’T’,’ ‘),1, 19), ‘RRRR-MM-DD HH24:MI:SS.FF’),”
    cQuery += “‘”+oJData:GetJSonObject(‘bank’)+”‘,”
    cQuery += “‘”+oJData:GetJSonObject(‘bankAccount’)+”‘,”
    cQuery += “‘”+oJData:GetJSonObject(‘bankAccountDigit’)+”‘,”
    cQuery += “‘”+oJData:GetJSonObject(‘ticketNumber’)+”‘,”
    cQuery += “‘”+oJData:GetJSonObject(‘liquidationStatus’)+”‘,”
    cQuery += “TO_TIMESTAMP(substr(replace(‘”+oJData:GetJSonObject(‘originalDueDate’)+”‘,’T’,’ ‘),1, 19), ‘RRRR-MM-DD HH24:MI:SS.FF’),”
    cQuery += cValToChar(oJData:GetJSonObject(‘discount’))+”,”
    cQuery += “‘”+oJData:GetJSonObject(‘sellerName’)+”‘,”
    cQuery += “)”

    Conout(” ***** gravaPaymentReconciliation[PAYMENTREC] – “+ cQuery)

    If (TcSqlExec(cQuery) 0)
    Conout(TCSQLError())
    _Msg := “ERRO ao gravar PAYMENTREC na operationid – ” + oJData:GetJSonObject(‘ID’)
    Conout(” ***** ERRO! gravar PAYMENTREC[PAYMENTREC] – ” + _Msg )
    Return .F.
    EndIf
    FreeObj(oJData)

    Next

    Porém qualquer leitura que eu faça do objeto retorna nulo.
    Exemplos:
    oJsonRet:GetJSonObject(‘total’) retorna nulo, como as outras do “cabeçalho”.
    o array data dentro do json, tb fica null.
    Pode me dar uma luz?
    Grato.

    Curtido por 1 pessoa

    • OLá Elieber, bom dia,

      Experimente fazer as alterações abaixo, e debugar o seu fonte :

      aData := oJsonRet[“data”]
      For i := 1 to Len(aData)
      oJData := aData[i]
      ….
      E, para pegar os valores das propriedades, use a notação oObjeto[“proproedade”], ela é mais amigável 😀

      Dê também uma olhada no fonte zJsonKit.PRW no Github, ele é um programa de exemplo que faz o parser de um Json ( string ou arquivo ) e monta um exemplo de acesso dos elementos e propriedades.

      Abraços e boa sorte 😀

      Curtir

  14. Bom dia,
    Infelizmente não funcionou dessa forma.
    Tentei de várias formas:
    Tem algum email que posso te enviar uma evidência?
    Grato,
    Elieber

    Curtido por 1 pessoa

  15. Boa Tarde Júlio,

    o Retorno do meu Json esta vindo da seguinte maneira:
    {
    “Produtos:”: [
    {
    “Preco”: “10,00”,
    “Saldo”: “4”,
    “Conv”: 1,
    “Produto”: “T-010061”
    }
    ]
    }
    Pergunta? tem alguma forma do Preço vir no formato numérico sem as Aspas, se eu deixar o campo sem a função cValToChar ele vem porem se o decimal

    Fiz assim: aListCli[nAux][‘Saldo’] := cvaltochar(TMPSB2->B2_QATU)

    Curtido por 1 pessoa

    • Hum, vejamos … O formato numérico em Json somente coloca os decimais se o valor vindo no AdvPL possui casas decimais … por exemplo, veja o fonte abaixo:

      user function blog1()
      oJson := jsonobject():New()
      oJson[‘Preco’] := 10.10
      conout(oJson:tojson())
      return

      A String gerada é “{“Preco”:10.1}” . Caso o valor nao tenha decimais, é gerada uma string apenas com o valor 10, todas sem as aspas 😀

      Abraços

      Curtir

  16. Júlio, parabéns pelos posts… ajudam muito.

    No caso do JSON, eu consulto uma API e quando dá erro ela retorna um JSON com várias informações.
    Há alguma função que eu consiga formatar esse JSON e mandar por e-mail de uma forma mais amigável, considerando que eu não conheço a estrutura do JSON que retorna?

    Curtido por 1 pessoa

  17. Julio, ótimo artigo sempre mandando muito bem!
    Eu tenho uma duvida, é possível usar o protocolo graphQL com a classe JSonObject() ou. tem alguma outra maneira de fazer isso em ADVPL?

    Muito obrigado desde já

    abs

    Curtir

    • Rapaiz …. pelo que eu pesquisei aqui, a resposta de uma requisição graphQL é no formato JSON ,.. Porem, o protocolo da requisição é WebSocket .. Nao sei dizer se tem alguma implementação nativa em Advpl que use WebSocket …

      Curtido por 1 pessoa

Deixe um comentário