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 \” e \\ . 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
- JSON. In: WIKIPÉDIA, a enciclopédia livre. Flórida: Wikimedia Foundation, 2019. Disponível em: <https://pt.wikipedia.org/w/index.php?title=JSON&oldid=56559658>. Acesso em: 25 out. 2019.
- JSON (especificação) – Disponível em <https://www.json.org/>. Acesso em Acesso em: 25 out. 2019.
- TDN – Classe tJsonObject
Pingback: JSON – O que é e como usar em AdvPL – Parte 02 | Tudo em AdvPL
Parabéns pelo post, me ajudou a desenvolver uma rotina exatamente com esse tema ! Obrigado !
CurtirCurtido por 1 pessoa
😀 Magina ..rs… é legal saber que os posts estão sendo úteis e bem aproveitados !!! 😀
CurtirCurtir
Obrigado pelo Post!
CurtirCurtido por 1 pessoa
Impressionante a sua capacidade de simplificar coisas que parecem ser difíceis.
“Se você não consegue explicar algo de modo simples é porque não entendeu bem a coisa.”
Albert Einstein
CurtirCurtido por 1 pessoa
Tkssss 😀
CurtirCurtir
Pingback: MongoDB em AdvPL – Prova de Conceito | Tudo em AdvPL
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”}]}
CurtirCurtido 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
CurtirCurtir
Pingback: JSON – O que é e como usar em AdvPL – Parte 03 – zJsonKit | Tudo em AdvPL
É obrigatório descartar o objeto Json no final?
CurtirCurtido por 1 pessoa
Opa, beleza ? Não é obrigatório, mas é “elegante” 😀
CurtirCurtir
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.
CurtirCurtido por 1 pessoa
Hummm… interessante, deixe-me fazer uns testes e ver o que eu descubro !!
CurtirCurtir
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”] 😀
CurtirCurtir
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?
CurtirCurtido 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 😀
CurtirCurtir
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 (?!)
CurtirCurtido 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 😉
CurtirCurtir
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
CurtirCurtido 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”
😀
CurtirCurtir
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’])
😀
CurtirCurtir
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”}}
CurtirCurtido 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á 😀
CurtirCurtir
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 😀
CurtirCurtido 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 😀
CurtirCurtido por 1 pessoa
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
CurtirCurtido por 1 pessoa
Parabéns fera !!!! Lembre-se, a primeira implementação a gente “faz funcionar”, na segunda a gente passa a limpo a primeira 😀
CurtirCurtido por 1 pessoa
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…
CurtirCurtido por 1 pessoa
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
CurtirCurtido por 1 pessoa
Olá Rafael, bom dia,
A estrutura de um objeto JSON é parecida hierarquicamente com um XML. Com o objeto gerado a partir dessa string, voce tem 3 propriedades: 2 com strings, e uma com arrays de objetos. De uma olhada nos exemplos do post e na TDN. — como por exemplo o método GetNames() — https://tdn.totvs.com/display/tec/JsonObject%3AGetNames
Abraços !!!
CurtirCurtir
E aproveite e dê também uma olhada nesse link aqui : https://siga0984.wordpress.com/2019/12/01/json-o-que-e-e-como-usar-em-advpl-parte-03-zjsonkit/ — tem uma ferramenta de apoio em AdvPL para mostrar o caminho de extração dos dados de um JSON em AdvPL !!
CurtirCurtir
Olá Júlio,
Parabéns pela qualidade dos seus posts!!!
Muito bom!
CurtirCurtido por 1 pessoa
😀 Opa, e eu agradeço a audiência 😉
CurtirCurtido por 1 pessoa
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
}
}
]
CurtirCurtido 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.
CurtirCurtir
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!
CurtirCurtido 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 😀
CurtirCurtido por 1 pessoa
Olá Júlio,
Antes de tudo, agradeço muito sua atenção comigo e com os demais. Você é um exemplo de profissional, que ama o que faz. Parabéns.Valeu muito a ajuda!
Uma outra pergunta: você tem algum material sobre o ADVPL usando REST (com os 4 verbos mais usados)?
Obrigado!!
CurtirCurtido por 1 pessoa
Olá Roger, magina ..rs.. 😀
Então, sobre REST eu ainda não publiquei nada, está na minha lista de pendências ..rs.. mas tem um post deveras interessante ( https://mvcadvpl.wordpress.com/2017/04/16/introducao-ao-webservice-rest-no-erp-protheus) , que deve atender a sua necessidade, além da documentação disponivel na TDN 😀
Agradeço a audiência, qualquer dúvida estou a disposição ! Abraços 😀
CurtirCurtido por 1 pessoa
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.
CurtirCurtido 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 😀
CurtirCurtir
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
CurtirCurtido por 1 pessoa
Opa, claro ! siga0984@gmail.com 😀
CurtirCurtir
Fala Júlio, cara esse post me tirou das trevas, muito fácil e forma de expressar e apresentar os exemplos.
CurtirCurtido por 1 pessoa
Opa ! Obrigado ..rs.. 😀
CurtirCurtir
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)
CurtirCurtido 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
CurtirCurtir
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?
CurtirCurtido por 1 pessoa
Opa, eu acho que sim… vou dar uma pesquisada, assim que tiver novidades eu posto aqui !! 😛
CurtirCurtir
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
CurtirCurtir
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 …
CurtirCurtido por 1 pessoa
Valeu Julião, sempre prestativo
abraços
CurtirCurtido por 1 pessoa