Introdução
Quando utilizamos APIs REST no Sage X3, geralmente precisamos enviar e receber informações através de uma string no formato JSON. A falta de um método nativo do runtime faz com que precisemos garantir que, em uma situação de envio, o JSON seja montado na estrutura correta, com vígurlas e aspas devidamente posicionadas ou, em uma situação de leitura de uma resposta da API, os valores das propriedades sejam corretamente extraídos.
Em ambos os casos, o framework nos obriga a utilizar funções próprias de manipulação de string, o que pode ser muito custoso em termos de desenvolvimento.
Felizmente, o Standard desenvolveu um método para serializar e desserializar JSONs utilizando classes do X3, de modo que podemos facilmente converter um JSON em uma estrutura de dados em árvore e vice-versa.
A classe ASYRJSO
Antes de ir para a solução em si, precisamos entender um pocuo sobre a classe standard onde a resolução do problema foi baseada.
No dicionário de classes (GESACLA) encontraremos a classe do standard ASYRJSO com a seguinte estrutura:
A estrutura dessa classe nos mostra que ela representa um nó de uma estrutura de dados do tipo árvore da seguinte maneira:
Onde as propriedades são:
· PRO – Char(15) – Nome da propriedade
· TYP – Integer – Tipo do nó. Pode assumir os seguintes valores:
o 1 – Object
o 2 – Array
o 3 – String
o 4 – Number
o 5 – Boolean
· CLB – Instance ASYRJSOCLB – Conteúdo do JSON. O X3 armazena todo o conteúdo nessa variável.
· START – Interger – Guarda o índice onde o conteúdo da propriedade PRO pode ser encontrado na string CLB
· LENGTH – Integer – Refere-se ao tamanho do conteúdo da propriedade. O sistema irá recuperar uma string dentro de CLB, do tamanho LENGTH e iniciando na posição definida por START.
· NEX – Instance ASYRJSO – Ponteiro para a proxima propriedade. Se não houver, o valor será null.
· SUB – Instance ASYRJSO – Ponteiro para a propriedade filha. Se não houver, o valor será null.
· FAT – Instance ASYRJSO – Ponteiro para propriedade pai. Se não houver, o valor será null.
Relação entre JSON e a classe ASYRJSO
Se a classe ASYRJSO pode ser usada como um nó de uma árvore, podemos mapear um JSON da mesma maneira. Como exemplo, vamos utilizar o seguinte JSON:
Utilizando a estrutura de dados em árvore, o JSON acima poderia ser representado da seguinte maneira:
E se admitirmos que cada nó da árvore acima representa uma instância da classe ASYRJSO, pegando como exemplo o nó da propriedade “texto”, as propriedades da instância estariam com os seguintes valores:
· PRO: “texto”
· TYP: 3 (String)
· CLB: “teste”20true”prop. do objeto”30123. Isto se refere à todos os valores de cada propriedade do JSON.
· START: 1, visto que o valor que queremos começa já na primeira posição do conteúdo de CLB.
· LENGTH: 7, pois “texto” possui 7 caracteres contando com as aspas duplas.
· NEX: Ponteiro para o nó seguinte que, no caso, é o da propriedade “valor”
· SUB: null, pois não há nenhum nó em um nível abaixo do nó em questão. No caso do nó cuja propriedade é “obj1”, o valor de SUB apontaria para o nó de proprieadede “propObj”.
· FAT – Ponteiro para o nó raiz.
Transformando o JSON em árvore
Considerando o mesmo JSON, como poderíamos transformá-lo nessa estrutura em árvore de modo simples?
Para isso, o standard fornece um método específico para essa operação chamado PARSEJSON do tratamento ASYRJSONPARSE. Podemos invocá-lo da seguinte maneira:
Analisando cada elemento da linha acima, temos:
· RET – Integer – Recebe o resultado da operação de conversão de JSON em NODES. Se a conversão ocorreu sem problemas, retorna-se 0 ([V]CST_AOK). Caso contrário, um valor positivo como 3 ([V]CST_AWARNING) ou 4 ([V]CST_AERROR).
· ACTX – Instância – Contém informações sobre contexto, como a mensagem do erro causado caso o retorno seja 4.
· NODES – Lista de Instância do tipo ASYRJSO – Ponteiro para o nó raiz da estrutura em árvore.
· NODE – Instância do tipo ASYRJSO – Ponteiro para o primeiro nó diferente do raiz. No caso, o nó cuja propriedade é “texto”.
O código completo seria dessa maneira:
Local Clbfile JSON
Local Instance NODES(1..) Using C_ASYRJSO
Local Instance NODE Using C_ASYRJSO
Local Instance ACTX Using =[V]CST_C_NAME_CLASS_CONTEXT
[L]ACTX = evalue(“NewInstance “+[V]CST_C_NAME_CLASS_CONTEXT+” AllocGroup NODE”)
Local Integer RET
JSON = ‘{“texto”:”teste”,”valor”:20,”booleano”:true,”obj1″:{“propObj”:”prop. do objeto”,”valObj”:30},”chaves”:[1,2,3]}’
[L]RET = func ASYRJSONPARSE.PARSEJSON([L]ACTX,[L]JSON,[L]NODES,[L]NODE)
Inspencionando a variável NODE pelo debugger podemos verificar que suas propriedades e valores se encontram da seguinte maneira:
No caso de erro, podemos utilizar a instância [L]ACTX para verificar o que aconteceu.
Modificando nosso JSON de exemplo, vams acrescentar uma vírgula em um lugar inadequado de modo a deixá-lo inválido:
Após a chamada de ASYRJSONPARSE.PARSEJSON, o valor de [L]RET será diferente de 0 e, via debugger, podemos verificar que a propriedade AERROR contém as informações sobre o problema:
Recuperando valores das propriedades
Agora que temos o JSON mapeado em classes, precisamos de uma maneira de recuperar o valor de cada propriedade. Como poderíamos recuperar o valor “teste” da propriedade “texto”?
Considerando o fato de nossa estrutura ser do tipo árvore, podemos implementar um algoritmo de busca em largura ou busca em profundidade de modo a encontrar a propriedade requerida e, uma vez encontrada, recuperar o valor utilizando o método do standard chamado STRINGIFY do tratamento ASYRJSO da seguinte maneira:
Funprog GET_VALUE(NODE,PRO)
Variable Instance NODE Using C_ASYRJSO
Value Char PRO
Local Clbfile RET
# Encontramos a propriedade cujo nome é PRO?
If NODE.PRO = PRO
#Utilizando o método do standard para recuperar o valor de um nó
Call STRINGIFY(NODE, RET) From ASYRJSO
#Removendo aspas duplas no caso de um nó ser do tipo string
If NODE.TYP = 3
[L]RET = mid$([L]RET,2,len([L]RET)-2)
Endif
Else
# Descemos um nível
If RET = [V]AVOID.ACHAR & NODE.SUB <> null
[L]RET = func GET_VALUE(NODE.SUB, PRO)
Endif
# Mudança para o nó vizinho
If RET = [V]AVOID.ACHAR & NODE.NEX <> null
[L]RET = func GET_VALUE(NODE.NEX, PRO)
Endif
Endif
#Retornamos o valor do nó
End [L]RET
O método acima que chamamos de GET_VALUE implementa uma busca em profundidade e retorna o valor do primeiro nó partindo de NODE cuja propriedade é PRO. No caso do nosso exemplo, se chamássemos o método da maneira abaixo:
Infbox func GET_VALUE(NODE, “texto”)
Obteríamos o seguinte resultado:
O método STRINGIFY pode ser usado em um nó de qualquer tipo. Isso facilita no caso em que queremos recuperar parte ou todo o JSON que mapeamos em classes. Ao chamarmos o método no nó raiz, obtemos o JSON completo como string, fazendo assim a operação reversa do método PARSEJSON como mostrado abaixo:
Local Clbfile JSON2
Call STRINGIFY([L]NODES, [L]JSON2) From ASYRJSO
Infbox [L]JSON2
A execução do código acima resulta na mensagem abaixo:
Conclusão
Vimos que é viável mapear um JSON inteiro em uma estrutura em árvore utilizando um único método. Além disso, evidenciamos que a recuperação de valores dessa estrutura é simplificada, eliminando assim a necessidade de realizar manipulações extensas em strings.
Com essas operações fundamentais à disposição, a manipulação de respostas provenientes de APIs REST torna-se mais eficiente. A preocupação com o início ou término de uma propriedade no JSON é completamente abstraída, proporcionando uma abordagem mais descomplicada para lidar com tais dados. Essa abstração oferece vantagens substanciais em termos de legibilidade, manutenção e confiabilidade do código, destacando a superioridade do método de manipulação baseado em