Json, Unicode and Ada strings mess…

Hi,

For a toy project, I wanted to parse some JSON with Ada.

A search in Alire crates suggested me to use json-ada. I used it, it seemed to work well, but as stated on the github page of the project, ‘Unicode may not be supported yet’ (as JSON text are required to be Unicode, this choice seems weird to me). In fact, it just doesn’t work with Unicode and I needed it…

Then I found GNATCOLL.Json. The library seemed quite similar to json-ada in its use and seemed to support UTF-8 encoded strings. At least, they were using UTF8_String, UTF8_Unbounded_String and UTF8_XString types from GNATCOLL.Strings. So I switched to it. It seemed to work well, but then I realised that I could only read JSON from String or Unbounded_String. Maybe I miss something (strings in Ada are such a mess), but I still don’t understand how to use a wide_string or wide_wide_string as input…

Then I found VSS.Json. It seems to be Unicode compatible. I didn’t switched for the moment as it’s quite different to use. But the last release is one year ago and there are changes in the API pending in master. So I would also have to change my code for the next release of VSS.

Am I missing some better JSON library? What are you using?

Thanks

I’m not as familiar with JSON libraries, but if you can’t find a good out of the box solution, I could suggest:

The UXStrings library by Blady @ GitHub - Blady-Com/UXStrings: Ada Unicode Extended Strings

It has unicode conversions. The reason I suggest this is you could take the original JSON-Ada library and change the top level package to use the UXStrings main string type (UXString) instead of the standard String type.

with UXStrings;
package JSON is
   subtype String is UXStrings.UXString;
end JSON;

That’ll globally change all the string types in the JSON library to UXStrings, which is unicode compatible. At that point you may have to doctor any lines with string or character constants to call the conversion functions (To_Unicode, From_Unicode for example). You may also have to remove any “Pure” pragmas in the packags as well as I had to remove it from the top level to support UXStrings.

It’s not out of the box, but if you like the JSON-Ada api, it might be worth playing around with.

If you want to use VSS - just clone repository and use it. I don’t known is there any plans to do official release, except it will be necessary for GNATstudio or ALS release, or someone will want to update Alire crate.

Thank you for the suggestion. However, there are a lot of character comparison with Latin_1 in JSON-Ada. I think I will go the VSS route. My main goal is not to write a JSON parser and it’s not as if I had a large code base to switch.

And if I’m not satisfied with VSS, I’ll try ASAP-JSON. I didn’t knew there was a competitor for alire.

I’m currently using VSS with unicode to parse json. The catch is that unlike json-ada which automatically parses the whole json structure for you, you need to build out exactly what you’re expecting in VSS, you’re basically coding the parser by hand.

I found documentation on VSS to be lacking, and kind of just figured out how to do this stuff by reading source files, so maybe this will help: I’m working on a library to parse Simplefin JSON structures in Ada; I just published the code on github (but don’t use it- it’s not in prerelease yet):

As you can see in the repo, I just used alire via alr with vss and it brought in VSS for me automatically. I only use alire for my coding. The actual parsers are located in simplefin-parsers.ads and simplefin-parsers.adb.

If I have to parse a transaction that looks like

{
  "id": "12394832938403",
  "posted": 793090572,
  "amount": "-33293.43",
  "description": "Uncle Frank's Bait Shop",
  "pending": true,
  "extra": {
    "category": "food"
  }
}

Then I do this in that part of the parser:

   procedure Read_Transaction
      (Reader  : in out VSS.JSON.Pull_Readers.JSON_Pull_Reader'Class;
       Item    : out Simplefin.Types.Transaction;
       Cur     : Cashe.Currency_Handling.Currency_Data;
       Success : in out Boolean) is
   begin
      while Success loop
         case Reader.Read_Next is
            --  Start of the next account object
            when VSS.JSON.Pull_Readers.Key_Name =>
               if Reader.Key_Name = "id" then
                  case Reader.Read_Next is
                     when VSS.JSON.Pull_Readers.String_Value =>
                        Item.Id := Reader.String_Value;

                     when others =>
                        Success := False;
                  end case;

               elsif Reader.Key_Name = "posted" then
                  case Reader.Read_Next is
                     when VSS.JSON.Pull_Readers.Number_Value =>
                        Item.Posted :=
                           Ada.Calendar.Conversions.To_Ada_Time
                              (Interfaces.C.long
                                 (VSS.JSON.As_Integer (Reader.Number_Value)));

                     when others =>
                        Success := False;
                  end case;

               elsif Reader.Key_Name = "amount" then
                  case Reader.Read_Next is
                     when VSS.JSON.Pull_Readers.String_Value =>
                        declare
                           use Cashe;
                           use Cashe.Money_Handling;
                           D : Decimal;
                        begin
                           Read_Decimal (Reader.String_Value, D, Success);
                           if Success then
                              Item.Amount := From_Major (D, Cur);
                           end if;
                        end;

                     when others =>
                        Success := False;
                  end case;

               elsif Reader.Key_Name = "description" then
                  case Reader.Read_Next is
                     when VSS.JSON.Pull_Readers.String_Value =>
                        Item.Description := Reader.String_Value;

                     when others =>
                        Success := False;
                  end case;

               elsif Reader.Key_Name = "memo" then
                  case Reader.Read_Next is
                     when VSS.JSON.Pull_Readers.String_Value =>
                        Item.Memo := Reader.String_Value;

                     when others =>
                        Success := False;
                  end case;

               elsif Reader.Key_Name = "payee" then
                  case Reader.Read_Next is
                     when VSS.JSON.Pull_Readers.String_Value =>
                        Item.Payee := Reader.String_Value;

                     when others =>
                        Success := False;
                  end case;

               elsif Reader.Key_Name = "pending" then
                  case Reader.Read_Next is
                     when VSS.JSON.Pull_Readers.Boolean_Value =>
                        Item.Pending := Reader.Boolean_Value;

                     when others =>
                        Success := False;
                  end case;

               elsif Reader.Key_Name = "extra" then
                  --  Todo
                  Skip_Object (Reader);

               else
                  Success := False;
               end if;

            when VSS.JSON.Pull_Readers.Start_Object =>
               null;

            when VSS.JSON.Pull_Readers.End_Object =>
               exit;

            when others =>
               Success := False;
         end case;
      end loop;
   end Read_Transaction;

You can check out the source for other things I’m doing like reading json arrays or other such stuff.

Good luck!

1 Like

Yes, VSS documentation is a bare minimum and doesn’t help much. But useful examples on how to use the library are given.

And thanks for the link.

I use gnatcoll.json.
Works well. I use it from strings only
/Björn

Yes, I succeeded to make gnatcoll work. Just had to do the right encode/decode before and after.

But xmlada dependency pulled by gnatcoll is not working well with alire. Each time I build my project, xmlada needs some reconfiguration. On my linux (old) personal desktop, it is fast (seconds). But on my windows (recent) work laptop, it is really slow (minutes!) and annoying.

Could be the same problem described here?

Yes. But it’s a 1 year old issue. Not sure it will be fixed soon.