This is an old revision of the document!


Introduction

The [http://json.org/ JSON] data format has grown to become quite popular in these past years. It is lightweight, fairly easy to both read and write and a lot less “bloated” than it's heavy XML cousin. Granted there's a lot of things you can do with XML which just isn't possible with JSON, but a lot of those things are rarely used, and if all you're looking for is a light data-interchange format, then JSON might just be what you need.

And luckily Ada can handle JSON just fine.

On this page you'll find an example on how to handle JSON reading and writing using the [http://libre.adacore.com/libre/tools/gnat-component-collection/ GNATColl] package from AdaCore. We're going to build a very simple JSON object that contains some company related information and we're going to read a JSON object from a file and output its fields using an iterator.

Obviously this program depends on you having GNATColl installed on your system. The GNATColl SVN URL is available in the JSON_Test README file. See the GNATColl documentation for how to install it.

The Program

You can clone the JSON_Test program from this Git repository:

git:github.com/ThomasLocke/JSON_test.git The commands to clone, compile and execute JSON_test are: $ git clone git:github.com/ThomasLocke/JSON_test.git $ cd JSON_test $ make $ cd exe/ $ ./json_test

If you don't feel like using Git, the code can be copied from below. I will of course do my best to make sure that the code on this page matches the code in the Git repository, but since this is a Wiki where anybody can make changes, I cannot guarantee the safety of running the code from this page, so use it with caution. I 'strongly' recommend cloning from the Git repository.

Some GNATCOLL.JSON Basics

When reading and writing JSON objects using GNATCOLL.JSON, you will mostly be interacting with the following types:

<pre lang=“ada”> type JSON_Value_Type is

(JSON_Null_Type,
 JSON_Boolean_Type,
 JSON_Int_Type,
 JSON_Float_Type,
 JSON_String_Type,
 JSON_Array_Type,
 JSON_Object_Type);

subtype UTF8_String is String;

type JSON_Value is tagged private; type JSON_Array is private; </pre>

The <tt>JSON_Value_Type</tt> lists the possible types of data you can add to or read from a JSON object. The <tt>UTF8_String</tt> type is used whenever you're dealing with string content. The <tt>JSON_Value</tt> type holds the actual JSON fields and data and it can even hold other <tt>JSON_Value</tt> objects. The <tt>JSON_Array</tt> type is used when you need to add arrays of data to a <tt>JSON_Value</tt> object.

So, how would we go about creating and outputting a JSON object like this one:

<pre lang=“javascript”> {“foo”:“bar”} </pre>

The Ada programming needed to create and output that JSON object is only a handful of lines in total:

<pre lang=“ada”> with Ada.Text_IO; with GNATCOLL.JSON;

procedure FooBar is

 use Ada.Text_IO;
 use GNATCOLL.JSON;
 FooBar_JSON : JSON_Value := Create_Object;

begin

 Set_Field (Val        => FooBar_JSON,
            Field_Name => "foo",
            Field      => "bar");
 Put_Line (Write (Foo_Bar_JSON));

end FooBar; </pre>

And that is all! First we initialize the empty <tt>FooBar</tt> <tt>JSON_Value</tt> object, then we call <tt>Set_Field</tt> to add a field (<tt>foo</tt>) and a value (<tt>bar</tt>) to it and finally we call <tt>Write</tt> to turn the <tt>JSON_Value</tt> object into a string.

What if we wanted to add some very important persons to a JSON array?

<pre lang=“ada”> with Ada.Text_IO; with GNATCOLL.JSON;

procedure FooBar is

 use Ada.Text_IO;
 use GNATCOLL.JSON;
 FooBar_JSON : JSON_Value := Create_Object;
 VIPS        : JSON_Array;

begin

 Set_Field (Val        => FooBar_JSON,
            Field_Name => "foo",
            Field      => "bar");
 Append (Arr => VIPS,
         Val => Create ("Arthur"));
 Append (Arr => VIPS,
         Val => Create ("Lancelot"));
 Set_Field (Val        => FooBar_JSON,
            Field_Name => "vips",
            Field      => VIPS);
 Put_Line (Write (Foo_Bar_JSON));

end FooBar; </pre>

And then we get:

<pre lang=“javascript”> {“vips”:[“Arthur”, “Lancelot”], “foo”:“bar”} </pre>

Reading data from the JSON object is done using a handful of <tt>Get</tt> functions:

<pre lang=“ada”> Put_Line (Get (Val ⇒ FooBar_JSON,

             Field => "foo"));

</pre>

Which obviously outputs “bar”.

There are also functions available to get the length of a JSON array and to return the value at a given index of an array. If you need to iterate all fields in a JSON object, you can use the <tt>Map_JSON_Object</tt> procedure. And lastly you can read a string JSON object and turn it into a <tt>JSON_Value</tt>.

Most of these things are shown in the JSON_Test program below, so without further ado, lets move on to that.

json_test.adb

This is the main program file. There's not a whole lot JSON action going on here. Most of that is happening in the Helpers package.

<pre lang=“ada”>


– – – JSON_Test – – – – Copyright (C) 2011, Thomas Løcke – – – – JSON_Test is free software; you can redistribute it and/or modify – – it under terms of the GNU General Public License as published by the – – Free Software Foundation; either version 2, or (at your option) any – – later version. JSON_Test is distributed in the hope that it will be – – useful, but WITHOUT ANY WARRANTY; without even the implied warranty of – – MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU – – General Public License for more details. You should have received a – – copy of the GNU General Public License distributed with JSON_Test. – – If not, write to the Free Software Foundation, 51 Franklin Street, – – Fifth Floor, Boston, MA 02110 - 1301, USA. – – –


with Ada.Text_IO; with GNATCOLL.JSON; with Helpers;

procedure JSON_Test is

 use Ada.Text_IO;
 use GNATCOLL.JSON;
 use Helpers;
 Company_JSON : JSON_Value := Create_Object;
 --  Initialize an empty JSON_Value object. We will add values to this
 --  object using the GNATCOLL.JSON.Set_Field procedures.
 Test_JSON    : JSON_Value;
 --  Uninitialized JSON_Value. We'll use this to hold the JSON object read
 --  from the exe/test.json file.

begin

  1. - Build the company JSON. See the Helpers package.

Set_Core_Company_Data (Obj ⇒ Company_JSON,

                        Name    => "AdaHeads K/S",
                        VAT_No  => "134679-zoink",
                        Hours   => "24/7",
                        Product => "Software development.",
                        Web     => "adaheads.com");
 Add_Company_Address (Obj         => Company_JSON,
                      Street_Name => "Mosekrogen",
                      Street_No   => 3,
                      Zip_Code    => "3520",
                      City_Name   => "Farum",
                      Phone       => "+45 11223344",
                      Comment     => "Headquarters");
 Add_Company_Address (Obj         => Company_JSON,
                      Street_Name => "Granvej",
                      Street_No   => 6,
                      Zip_Code    => "3660",
                      City_Name   => "Stenløse",
                      Phone       => "+45 22334455",
                      Comment     => "Branch office, Thomas Løcke");
 Add_Person (Obj   => Company_JSON,
             Name  => "Jacob Sparre Andersen",
             Title => "Codemonkey",
             Email => "jacspaand@somewhere.tld");
 Add_Person (Obj   => Company_JSON,
             Name  => "Kim Rostgaard Christensen",
             Title => "Codemonkey",
             Email => "kimroschr@somewhere.tld");
 Add_Person (Obj   => Company_JSON,
             Name  => "Thomas Løcke",
             Title => "Codemonkey",
             Email => "tholoc@somewhere.tld");
 Add_Person (Obj   => Company_JSON,
             Name  => "Ulrik Hørlyk Hjort",
             Title => "Codemonkey",
             Email => "ulrhorhjo@somewhere.tld");
  1. - Output the company JSON. Note that we've set the Compact parameter of
  2. - Write to False for slightly improved readability. The default is True.

New_Line;

 Put_Line ("--> Company_JSON Start <--");
 Put_Line (Write (Company_JSON, False));
 Put_Line ("--> Company_JSON End <--");
 New_Line;
  1. - Load the test.json JSON object into the Test_JSON object.

Test_JSON := Read (Strm ⇒ Load_File (“test.json”),

                    Filename => "");
  1. - Iterate the Test_JSON object. The Handler procedure is responsible for
  2. - outputting the contents.

Put_Line (”–> Test_JSON Start ←-”);

 Map_JSON_Object (Val   => Test_JSON,
                  CB    => Handler'Access);
 Put_Line ("--> Test_JSON End <--");
 New_Line;

end JSON_Test; </pre>

src/helpers.ads

It is in this package the actual building and iteration of the JSON object happens.

<pre lang=“ada”>


– – – Helpers – – – – SPEC – – – – Copyright (C) 2011, Thomas Løcke – – – – Helpers is free software; you can redistribute it and/or modify – – it under terms of the GNU General Public License as published by the – – Free Software Foundation; either version 2, or (at your option) any – – later version. Helpers is distributed in the hope that it will be – – useful, but WITHOUT ANY WARRANTY; without even the implied warranty of – – MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU – – General Public License for more details. You should have received a – – copy of the GNU General Public License distributed with JSON_Test. – – If not, write to the Free Software Foundation, 51 Franklin Street, – – Fifth Floor, Boston, MA 02110 - 1301, USA. – – –


with GNATCOLL.JSON;

package Helpers is

 use GNATCOLL.JSON;
 procedure Set_Core_Company_Data
   (Obj     : in out JSON_Value;
    Name    : in     UTF8_String;
    VAT_No  : in     UTF8_String;
    Hours   : in     UTF8_String;
    Product : in     UTF8_String;
    Web     : in     UTF8_String);
 --  There can only be one set of these data in the final JSON, so whenever
 --  this procedure is called, the previous set is overwritten with the 
 --  latest set.
 procedure Add_Company_Address
   (Obj         : in out JSON_Value;
    Street_Name : in     UTF8_String;
    Street_No   : in     Positive;
    Zip_Code    : in     UTF8_String;
    City_Name   : in     UTF8_String;
    Phone       : in     UTF8_String;
    Comment     : in     UTF8_String);
 --  A company can have several addresses, so these are added as JSON arrays.
 procedure Add_Person
   (Obj   : in out JSON_Value;
    Name  : in     UTF8_String;
    Title : in     UTF8_String;
    Email : in     UTF8_String);
 --  A company can have several employees, so these are added as JSON arrays.
 
 procedure Handler
   (Name  : in UTF8_String;
    Value : in JSON_Value);
 --  Output the contents of the Value JSON object.
 function Load_File
   (Filename : in String)
    return String;
 --  Load the test.json JSON object from Filename and return it as a string.
 

end Helpers; </pre>

src/helpers.adb

Simplicity at work! Using GNATCOLL.JSON it's a complete no-brainer to build JSON objects.

<pre lang=“ada”>


– – – Helpers – – – – BODY – – – – Copyright (C) 2011, Thomas Løcke – – – – Helpers is free software; you can redistribute it and/or modify – – it under terms of the GNU General Public License as published by the – – Free Software Foundation; either version 2, or (at your option) any – – later version. Helpers is distributed in the hope that it will be – – useful, but WITHOUT ANY WARRANTY; without even the implied warranty of – – MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU – – General Public License for more details. You should have received a – – copy of the GNU General Public License distributed with JSON_Test. – – If not, write to the Free Software Foundation, 51 Franklin Street, – – Fifth Floor, Boston, MA 02110 - 1301, USA. – – –


with Ada.Direct_IO; with Ada.Directories; with Ada.Text_IO;

package body Helpers is

  1. ————————–
  2. - Add_Company_Address –
  3. ————————–
 procedure Add_Company_Address
   (Obj         : in out JSON_Value;
    Street_Name : in     UTF8_String;
    Street_No   : in     Positive;
    Zip_Code    : in     UTF8_String;
    City_Name   : in     UTF8_String;
    Phone       : in     UTF8_String;
    Comment     : in     UTF8_String)
 is
    Address_JSON : constant JSON_Value  := Create_Object;
    Address_Arr  : JSON_Array;
 begin
    if Has_Field (Obj, "addresses") then
       Address_Arr := Get (Val   => Obj,
                           Field => "addresses");
    end if;
    --  If the Obj JSON object alread have an addresses field, then we've 
    --  added at least one address already. We then load the previously
    --  added addresses into the Address_Arr JSON_Array variable, and append
    --  to this later.
    Set_Field (Val        => Address_JSON,
               Field_Name => "street_name",
               Field      => Street_Name);
    --  Using the Set_Field procedure we add fields to the Address_JSON. In
    --  this case we add the field "street_name" and we give it the value of
    --  of Street_Name. There are Set_Field procedures for the valid JSON
    --  types. See GNATCOLL.JSON.JSON_Value_Type.
    Set_Field (Val        => Address_JSON,
               Field_Name => "street_no",
               Field      => Street_No);
    Set_Field (Val        => Address_JSON,
               Field_Name => "zip_code",
               Field      => Zip_Code);
    Set_Field (Val        => Address_JSON,
               Field_Name => "city",
               Field      => City_Name);
    Set_Field (Val        => Address_JSON,
               Field_Name => "phone",
               Field      => Phone);
    Set_Field (Val        => Address_JSON,
               Field_Name => "comment",
               Field      => Comment);
    Append (Arr => Address_Arr,
            Val => Address_JSON);
    --  Here we append the Address_JSON JSON_Value to the Address_Arr
    --  JSON_Array. Note that we could also have used &, but that would've
    --  been less efficient.
    Set_Field (Val        => Obj,
               Field_Name => "addresses",
               Field      => Address_Arr);
    --  Finally we add the Address_Arr JSON_Array to the Obj JSON_Value.
 end Add_Company_Address;
  1. —————–
  2. - Add_Person –
  3. —————–
 procedure Add_Person
   (Obj   : in out JSON_Value;
    Name  : in     UTF8_String;
    Title : in     UTF8_String;
    Email : in     UTF8_String)
 is
    Person_JSON : constant JSON_Value  := Create_Object;
    Person_Arr  : JSON_Array;
 begin
    if Has_Field (Obj, "persons") then
       Person_Arr := Get (Val   => Obj,
                          Field => "persons");
    end if;
    Set_Field (Val        => Person_JSON,
               Field_Name => "name",
               Field      => Name);
    Set_Field (Val        => Person_JSON,
               Field_Name => "title",
               Field      => Title);
    Set_Field (Val        => Person_JSON,
               Field_Name => "email",
               Field      => Email);
    Append (Arr => Person_Arr,
            Val => Person_JSON);
    Set_Field (Val        => Obj,
               Field_Name => "persons",
               Field      => Person_Arr);
 end Add_Person;
  1. —————
  2. - Iterator –
  3. —————
 procedure Handler
   (Name  : in UTF8_String;
    Value : in JSON_Value)
 is
    use Ada.Text_IO;
 begin
    case Kind (Val => Value) is
       when JSON_Null_Type =>
          Put_Line (Name & "(null):null");
       when JSON_Boolean_Type =>
          Put_Line (Name & "(boolean):" & Boolean'Image (Get (Value)));
       when JSON_Int_Type =>
          Put_Line (Name & "(integer):" & Integer'Image (Get (Value)));
       when JSON_Float_Type =>
          Put_Line (Name & "(float):" & Float'Image (Get (Value)));
       when JSON_String_Type =>
          Put_Line (Name & "(string):" & Get (Value));
       when JSON_Array_Type =>
          declare
             A_JSON_Array : constant JSON_Array := Get (Val => Value);
             A_JSON_Value : JSON_Value;
             Array_Length : constant Natural := Length (A_JSON_Array);
          begin
             Put (Name & "(array):[");
             for J in 1 .. Array_Length loop
                A_JSON_Value := Get (Arr   => A_JSON_Array,
                                     Index => J);
                Put (Get (A_JSON_Value));
                if J < Array_Length then
                   Put (", ");
                end if;
             end loop;
             Put ("]");
             New_Line;
          end;
       when JSON_Object_Type =>
          Put_Line (Name & "(object):");
          Map_JSON_Object (Val => Value,
                           CB  => Handler'Access);
    end case;
    --  Decide output depending on the kind of JSON field we're dealing with.
    --  Note that if we get a JSON_Object_Type, then we recursively call
    --  Map_JSON_Object again, which in turn calls this Handler procedure.
 end Handler;
  1. —————-
  2. - Load_File –
  3. —————-
 function Load_File
   (Filename : in String)
    return String
 is
    use Ada.Directories;
    File_Size    : constant Natural := Natural (Size (Filename));
    subtype Test_JSON_Str is String (1 .. File_Size);
    package File_IO is new Ada.Direct_IO (Test_JSON_Str);
    File           : File_IO.File_Type;
    String_Content : Test_JSON_Str;
 begin
    File_IO.Open (File => File,
                  Mode => File_IO.In_File,
                  Name => Filename);
    File_IO.Read (File => File,
                  Item => String_Content);
    File_IO.Close (File => File);
    return String_Content;
 end Load_File;
  1. —————————-
  2. - Set_Core_Company_Data –
  3. —————————-
 procedure Set_Core_Company_Data
   (Obj     : in out JSON_Value;
    Name    : in     UTF8_String;
    VAT_No  : in     UTF8_String;
    Hours   : in     UTF8_String;
    Product : in     UTF8_String;
    Web     : in     UTF8_String)
 is
 begin
    Set_Field (Val        => Obj,
               Field_Name => "company_name",
               Field      => Name);
    Set_Field (Val        => Obj,
               Field_Name => "vat_no",
               Field      => VAT_No);
    Set_Field (Val        => Obj,
               Field_Name => "hours",
               Field      => Hours);
    Set_Field (Val        => Obj,
               Field_Name => "product",
               Field      => Product);
    Set_Field (Val        => Obj,
               Field_Name => "web",
               Field      => Web);
 end Set_Core_Company_Data;

end Helpers; </pre>

exe/test.json

This is the test JSON object we'll read from file and then output using the <tt>Map_JSON_Object</tt> procedure.

<pre lang=“javascript”> {“widget”: {

  "debug": true,
  "window": {
      "title": "Sample Konfabulator Widget",
      "name": "main_window",
      "width": 500,
      "height": 500
  },
  "image": { 
      "src": "Images/Sun.png",
      "name": "sun1",
      "hOffset": 250.0,
      "vOffset": 250.37,
      "alignment": "center"
  },
  "text": {
      "data": ["Click Here","Klik Her"],
      "size": 36,
      "style": "bold",
      "name": "text1",
      "hOffset": 250,
      "vOffset": 100,
      "alignment": "center",
      "onMouseUp": "sun1.opacity = (sun1.opacity / 100) * 90;"
  }

}} </pre>

json_test.gpr

This file set up various compile options. It informs the compiler of the location of the source files and where to put the object and the executable files. Note that you can build the JSON_Test program using debug settings. This is simply done by calling <tt>make debug</tt> instead of plain <tt>make</tt>.

<pre lang=“ada”>


– – – JSON_Test Project File – – – – Copyright (C) 2011, Thomas Løcke – – – – JSON_Test is free software; you can redistribute it and/or modify – – it under terms of the GNU General Public License as published by the – – Free Software Foundation; either version 2, or (at your option) any – – later version. JSON_Test is distributed in the hope that it will be – – useful, but WITHOUT ANY WARRANTY; without even the implied warranty of – – MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU – – General Public License for more details. You should have received a – – copy of the GNU General Public License distributed with JSON_Test. – – If not, write to the Free Software Foundation, 51 Franklin Street, – – Fifth Floor, Boston, MA 02110 - 1301, USA. – – –


with “gnatcoll”;

Project JSON_Test is

 type Build_Type is ("Debug", "Production");
 Build : Build_Type := External ("BUILDTYPE", "Production");
 Source_Options := ("src");
 for Source_Dirs use Source_Options;
 for Main use ("json_test.adb");
 for Exec_Dir use "exe";
 case Build is
 
    when "Production" =>
       for Object_Dir use "build_production";
    when "Debug" =>
       for Object_Dir use "build_debug";
 
 end case;
 package Ide is
  1. - Adjust this to point to the compiler you want to use.

for Compiler_Command (“ada”) use “gnatmake”;

 end Ide;
 package Compiler is
    
    case Build is
    
       when "Production" =>
          for Default_Switches ("Ada") 
            use ("-gnatwa",
                 "-gnaty3abcdefhiklmnoprstux",
                 "-Wall",
                 "-O2",
                 "-gnat05");
       when "Debug" =>
          for Default_Switches ("Ada") 
            use ("-gnatwa",
                 "-gnata",
                 "-gnatVa",
                 "-gnaty3abcdefhiklmnoprstux",
                 "-Wall",
                 "-O1",
                 "-gnat05",
                 "-g");
                      
    end case;
 end Compiler;

end JSON_Test; </pre>

makefile

Ultra simple makefile.

<pre lang=“make”> ############################################################################### # # # JSON_Test # # # # Make File # # # # Copyright (C) 2011, Thomas Løcke # # # # JSON_Test is free software; you can redistribute it and/or modify it under # # terms of the GNU General Public License as published by the Free Software # # Foundation; either version 2, or (at your option) any later version. # # JSON_Test is distributed in the hope that it will be useful, but WITHOUT # # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or # # FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for # # more details. You should have received a copy of the GNU General Public # # License distributed with JSON_Test. If not, write to the Free Software # # Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110 - 1301, USA. # # # ###############################################################################

all:

gnatmake -P json_test

debug:

BUILDTYPE=Debug gnatmake -P json_test

clean:

gnatclean -P json_test
BUILDTYPE=Debug gnatclean -P json_test

</pre>

Program Output

When running the JSON_Test program, you should get output similar to this:

<pre> –> Company_JSON Start ←- {

  "company_name": "AdaHeads K/S",
  "web": "adaheads.com",
  "addresses": [
      {
          "comment": "Headquarters",
          "street_name": "Mosekrogen",
          "zip_code": "3520",
          "city": "Farum",
          "phone": "+45 11223344",
          "street_no": 3
      },
      {
          "comment": "Branch office, Thomas Løcke",
          "street_name": "Granvej",
          "zip_code": "3660",
          "city": "Stenløse",
          "phone": "+45 22334455",
          "street_no": 6
      }
  ],
  "product": "Software development.",
  "hours": "24/7",
  "persons": [
      {
          "name": "Jacob Sparre Andersen",
          "email": "jacspaand@somewhere.tld",
          "title": "Codemonkey"
      },
      {
          "name": "Kim Rostgaard Christensen",
          "email": "kimroschr@somewhere.tld",
          "title": "Codemonkey"
      },
      {
          "name": "Thomas Løcke",
          "email": "tholoc@somewhere.tld",
          "title": "Codemonkey"
      },
      {
          "name": "Ulrik Hørlyk Hjort",
          "email": "ulrhorhjo@somewhere.tld",
          "title": "Codemonkey"
      }
  ],
  "vat_no": "134679-zoink"

} –> Company_JSON End ←-

–> Test_JSON Start ←- widget(object): window(object): width(integer): 500 name(string):main_window height(integer): 500 title(string):Sample Konfabulator Widget image(object): hOffset(float): 2.50000E+02 vOffset(float): 2.50370E+02 name(string):sun1 alignment(string):center src(string):Images/Sun.png debug(boolean):TRUE text(object): hOffset(integer): 250 vOffset(integer): 100 style(string):bold size(integer): 36 data(array):[Click Here, Klik Her] name(string):text1 alignment(string):center onMouseUp(string):sun1.opacity = (sun1.opacity / 100) * 90; –> Test_JSON End ←- </pre>

It might not look exactly like above, but the contents should be the same. You can validate the <tt>Company_JSON</tt> object at [http://jsonlint.com/ jsonlint.com].

I hope you've enjoyed this short venture into the world of Ada and JSON. Feel free to make changes to this page if you find any mistakes or if you feel something is unclear.


Navigation