Return array from reading a json (question relates to unconstrained array type)

Suppose in the main procedure we have an unconstrained array type defined:

type My_array is array (Integer range <>) of Float;

I’d like to create a function (in another package), that reads a json file and returns an array of floats contained in that file.

I am unsure of a good way to approach this. One thought is to read the json file, find out how long that array is (returning an integer from a querying function). Then, back in main, use “declare” block to instantiate the array of the correct length.

Finally, fill the array by using a 2nd subprogram that re-reads the json, and returns the array using an “out” parameter.

This all feels a bit silly. What is a good approach?

You can implement the whole JSON spec in an afternoon.
The approach I’d recommend is using tagged-types and a few “helpers”:

Package JSON is 
   -- All the various types a JSON-object can be; 'Collection' instead of array.
   Type Form is (Boolean, Numeric, Object, Collection, ETC);

   -- JSON floats can only hold the numeric values; no INF or NaN.
   Type Real is Float range Float'Range;

   Type Base(Style : Form) is abstract tagged null record;
       -- If you want to use streams:
       Function Create (Params : not null access Ada.Streams.Root_Stream_Type'Class)
       return Base is abstract;

   -- All common JSON functions.

   Type JSON_Vector is (Positive range <>) of [not null access] Base'Class;
   -- You could use Indefinite_Vector.

  Type Number is new Base(Style => Numeric) with record
     Value : Real:= raise Format_Error with "Needs Initialization.";
  end record;

   Type JSON_Array( Length : Natural ) is record
     Value : JSON_Vector(1..Length);
   end record;

  -- Etc.

--Private-- Typically I'd hide the implementation…

  -- If you want to use streams:
  -- For Make, use Generic_Dispatching_Constructor; remember:
  --    generic
  --       type T (<>) is abstract tagged limited private;
  --       type Parameters (<>) is limited private;
  --       with function Constructor (Params : not null access Parameters) return T is abstract;
  --    function Ada.Tags.Generic_Dispatching_Constructor (The_Tag : Tag; Params  : not null access Parameters) return T'Class;
  -- 
  -- For Parameters, use: Ada.Streams.Root_Stream_Type'Class
  -- For Constructor, use: Create

   Function Make_Instance is new Generic_Dispatching_Constructor(
      T           => Base,
      Parameters  => Ada.Streams.Root_Stream_Type'Class
      Constructor => Create
     );
   
End JSON;

Then you make each actual instance of BASE’s descendants “know how to create itself” given access to the stream (or text+cursor, as you wish). (Hint: the ‘array’ type reads in until the closing ‘]’; the opening of the call, is supplying the tag.)

JSON array can contain any values. So you need to validate correct but invalid input. You also should recon that there might be no way to have several passes. E.g. JSON input may come from the keyboard stream.

The approach in here is that the JSON parser allocates JSON object in the arena in one pass. You can then inspect the object and throw away the arena when no more needed. Doing so you can construct an array from JSON_Value if that value is an array and all elements of are JSON numbers. The number of elements is the array length.

P.S. If you are not forced to use JSON, then do not. It is an awful way to store/communicate/serialize dart.

Actually in case of JSON value a definite variant record type is sufficient.