Manipulação de JSONs no Sage X3

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

Deixe um comentário

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *