JSON e Ada

Estou iniciando os estudos da linguagem Ada. E depois dos primeiros tutoriais, nos quais começamos com um simples “Hello World” e aos poucos vamos entrando nos detalhes da linguagem (veja meu post aqui), chegou a hora de aprender como trabalhar com coisas mais próximas do mundo real.

Eu estava curioso para saber como ler, e manipular arquivos JSON em ada. Assim, tentei procurar alguns tutoriais, os primeiros foram:

  • Ada Core, o mesmo site do GNAT Programming Studio
  • Rosetta Code, o site onde, segundo eles “a ideia é apresentar soluções de uma mesma tarefa no maior número de linguagens possível”.

Eu decidi tentar o exemplo do Rosetta Code, por ser um pouco mais completo.

Então vamos ao passo-a-passo:

Primeiro, inicie o GPS e clique em“Create New Project”

Escolha o Template: Simple Ada Project.

Na próxima tela, confirme o diretório no qual você deseja salvar o projeto, escolha o nome o projeto e o nome principal (Main Name).

Main name é o nome da procedure que será chamada no início da execução, conforme veremos em breve.

E você agora tem uma tela pronta para programar. Veja o nome da Procedure Json, como foi escolhido no passo anterior.

Eu reproduzirei todos os passos conforme eu fiz, mesmo tendo alguns erros. Eu mostrarei aqui como resolvê-los.

Copie e cole o código do site Rosetta Code, primeira opção que usa a biblioteca GNATCOOL. Se preferir, copie e cole o código abaixo, é o mesmo.

with Ada.Text_IO;
with GNATCOLL.JSON;

procedure JSON_Test is
use Ada.Text_IO;
use GNATCOLL.JSON;

JSON_String : constant String := "{""name"":""Pingu"",""born"":1986}";

Penguin : JSON_Value := Create_Object;
Parents : JSON_Array;
begin
Penguin.Set_Field (Field_Name => "name",
Field => "Linux");

Penguin.Set_Field (Field_Name => "born",
Field => 1992);

Append (Parents, Create ("Linus Torvalds"));
Append (Parents, Create ("Alan Cox"));
Append (Parents, Create ("Greg Kroah-Hartman"));

Penguin.Set_Field (Field_Name => "parents",
Field => Parents);

Put_Line (Penguin.Write);

Penguin := Read (JSON_String, "json.errors");

Penguin.Set_Field (Field_Name => "born",
Field => 1986);

Parents := Empty_Array;
Append (Parents, Create ("Otmar Gutmann"));
Append (Parents, Create ("Silvio Mazzola"));

Penguin.Set_Field (Field_Name => "parents",
Field => Parents);

Put_Line (Penguin.Write);
end JSON_Test;

Clique no botão “Build and Run”

Ao final, pode-se observar um erro e um aviso. Observe nas duas janelas inferiores.

Primeiro, vamos resolver o aviso (warning).

json.adb:4:11: warning: file name does not match unit name, should be "json_test.adb"

O problema é que demos o nome principal como “Json”, e ao copiar e colar o código o nome que veio é “JSON_Test”. Para resolver, basta alterar o nome da procedure para Json nas linhas 4 e 41:

4   procedure JSON is
...
...
...
40 Put_Line (Penguin.Write);
41 end JSON;

O erro:

json.adb:2:06: file "gnatcoll.ads" not found

A biblioteca GNATOOL é instalada juntamente com o GPS, mas é necessário informar que você irá usá-la. Para fazer isso, a partir do Project Explorer, clique na direita sobre o nome do projeto, e vá para Project → Properties:

Na janela de propriedades, selecione Dependencies e arraste para a janela de dependências a bilbioteca gnatcool, conforme pode ser visto na imagem abaixo:

Clique em Save, e você verá a biblioteca na árvore do projeto:

Vamos tentar compilar novamente. Mas…

Na janela inferior à direita, o GPS permite que você clique no erro para ir diretamente ao arquivo sugerido. No caso gnatcool-json.ads. Com isso fica mais fácil tentar descobrir o problema.

Em Ada, é possível usar Function Overloading, ou seja, declarar funções com o mesmo nome onde os parâmetros podem variar. A ideia é que o compilador possa escolher a função correta dependendo dos parâmetros utilizados, mas nesse caso, o compilador não identificou se o valor utilizado era do tipo Integer ou Long Integer. Lembre-se que Ada é uma linguagem fortemente tipada!

Para resolver isso, vamos usar type casting. (Agradeço ao Keith Thompson do Grupo de emails)

Penguin.Set_Field (Field_Name => "born",
Field => Integer'(1992));

Append (Parents, Create ("Linus Torvalds"));
Append (Parents, Create ("Alan Cox"));
Append (Parents, Create ("Greg Kroah-Hartman"));

Penguin.Set_Field (Field_Name => "parents",
Field => Parents);

Put_Line (Penguin.Write);

Penguin := Read (JSON_String, "json.errors");

Penguin.Set_Field (Field_Name => "born",
Field => Integer'(1986));

Clique em Build and Run… e SUCESSO!!

Entendendo o código

Agora, eu sugiro que você analise o código linha a linha.

O código é dividido em dois blocos: no primeiro o objeto JSON é criado desde o início, com as variáveis Penguin do tipoJSON_Value e Parents do tipoJSON_Array.

  1. Primeiro bloco de código

Os dois primeiros comandos criam os campos JSON"name" and "born"

Penguin.Set_Field (Field_Name => "name", 
Field => "Linux");

Penguin.Set_Field (Field_Name => "born",
Field => Integer'(1992))

O próximo passo é criar um Array:

Append (Parents, Create ("Linus Torvalds"));
Append (Parents, Create ("Alan Cox"));
Append (Parents, Create ("Greg Kroah-Hartman"));

E criar um outro campo JSON chamado"parents" cujo conteúdo é o Array criado

Penguin.Set_Field (Field_Name => "parents",
Field => Parents);

Para mostrar o resulto, é utilizado o comando Put_Line.

Put_Line (Penguin.Write);

2. Segundo bloco de código

Neste bloco o programa lê uma variável do tipo String chamada JSON_String cujo conteúdo é uma estrutura JSON:

JSON_String : constant String := "{""name"":""Pingu"",""born"":1986}";

Para enviar o conteúdo para uma variável do tipoJSON_Value :

Penguin := Read (JSON_String, "json.errors");

Agora, a variável Penguin tem a estrutura JSON:

{
"name": "Pingu",
"born": 1986
}

A próxima instrução atualiza o campo born. No código original, eles usam o mesmo valor. Eu sugiro que você tente outro valor para verificar como funciona.

   Penguin.Set_Field (Field_Name => "born",
Field => Integer'(1987));

Agora, variável Parents é esvaziada e são criados novos campos.

Parents := Empty_Array;
Append (Parents, Create ("Otmar Gutmann"));
Append (Parents, Create ("Silvio Mazzola"));

Penguin.Set_Field (Field_Name => "parents",
Field => Parents);

Put_Line (Penguin.Write);

Vamos explorar o funcionamento da biblioteca

Se você também é um iniciante, sugiro utilizar as funcionalidades do GPS, usando o clique da direita para ver as declarações de variáveis, dos tipos, explorar o arquivo gnatcool-json.ads, alterar valores, tentar utilizar outros tipos e estruturas JSON, etc.

Espero que tenha gostado e vamos continuar os estudos da linguagem Ada!