Thread overview
Casting JSONValues arrays to native arrays ... ??? ...
Sep 23, 2021
james.p.leblanc
Sep 23, 2021
james.p.leblanc
Sep 23, 2021
james.p.leblanc
Sep 24, 2021
james.p.leblanc
September 23, 2021

Dear D-ers,

In attempting to cast JSONValues that hold arrays to "native" array types,
I have hit some issues. Example code:

import std.stdio;
import std.json;

void main(){

   JSONValue jj;
   jj["d"] = [ 1.234 ];                  // a "dummy" double value
   jj["ba"] = [ true, false, true];      // "ba" boolean array

   writeln("typeid(jj): ", typeid(jj), ", jj: ", jj );

   // various things that I thought might work, but do NOT

   auto z1 = cast(bool)   jj["ba"];           // attempt #1
   auto z2 = cast(bool[]) jj["ba"];           // attempt #2
   auto z3 = cast(bool)   jj["ba"].array;     // attempt #3
   auto z4 = cast(bool[]) jj["ba"].array;     // attempt #4
   writeln("typeid(z4): ", typeid(z4), ", z4: ", z4 );

}

Attempts 1,2, and 3 yield compilation errors (which I somewhat understand):

question.d(12): Error: cannot cast expression jj.opIndex("ba") of type JSONValue to bool
question.d(13): Error: cannot cast expression jj.opIndex("ba") of type JSONValue to bool[]
question.d(14): Error: cannot cast expression jj.opIndex("ba").array() of type JSONValue[] to bool

However, if I comment out the offending attempts (1, 2, and 3), then it
compiles, and can run ... but produces a result which I very much do NOT
understand:

typeid(jj): std.json.JSONValue, jj: {"ba":[true,false,true],"d":[1.23399999999999999]}
typeid(z4): bool[], z4: [false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, true, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, true, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, true, false, false, false, false, false, false, false]

Hmmmm... is there a standard way to push these JSONValues into nice native
array types? (The real code is eventually going to be using traits and mixins
... but I do not think this should pose additional problems).

All help and illumination thankfully received.
Best Regards,
James

September 23, 2021

On 9/23/21 2:20 PM, james.p.leblanc wrote:

>

Dear D-ers,

In attempting to cast JSONValues that hold arrays to "native" array types,
I have hit some issues.  Example code:

import std.stdio;
import std.json;

void main(){

    JSONValue jj;
    jj["d"] = [ 1.234 ];                  // a "dummy" double value
    jj["ba"] = [ true, false, true];      // "ba" boolean array

Note that this creates a JSONValue array which copies the values of the boolean array, converting them to JSONValue (which is what a JSONValue array stores).

>

   writeln("typeid(jj): ", typeid(jj), ", jj: ", jj );

   // various things that I thought might work, but do NOT

   auto z1 = cast(bool)   jj["ba"];           // attempt #1

A JSONValue cannot be cast to a boolean (it does not provide the appropriate opCast)

>

   auto z2 = cast(bool[]) jj["ba"];           // attempt #2
   auto z3 = cast(bool)   jj["ba"].array;     // attempt #3

These try to cast something that is not an array to an array or vice versa. These are not supported unless the type itself has an opCast overload.

>

   auto z4 = cast(bool[]) jj["ba"].array;     // attempt #4

Casting one array type to another is like pointing at the array that represents the original type as if it were of the new type. No translation is made, you are pointing at the same memory! The length is adjusted based on the size of the original array element and the size of the new one.

For instance:

int[] arr = [1];
auto a2 = cast(ubyte[])arr;

assert(a2.length == 4);
assert(a2 == cast(ubyte[])([1, 0, 0, 0])); // assuming little endian
>

However, if I comment out the offending attempts (1, 2, and 3), then it
compiles, and can run ... but produces a result which I very much do NOT
understand:

typeid(jj): std.json.JSONValue, jj: {"ba":[true,false,true],"d":[1.23399999999999999]}
typeid(z4): bool[], z4: [false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, true, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, true, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, true, false, false, false, false, false, false, false]

This is an array of JSONValue, with each byte interpreted as if it were a bool.

>

Hmmmm... is there a standard way to push these JSONValues into nice native
array types? (The real code is eventually going to be using traits and mixins
... but I do not think this should pose additional problems).

How you really do this:

import std.algorithm : map;
auto z5 = jj["ba"] // get the JSONValue that is at the key "ba"
      .map!(v => v.get!bool) // map each value into a boolean
      .array // create an array out of the results;
assert z5 == [true, false, true];

(warning, untested)

-Steve

September 23, 2021

On Thursday, 23 September 2021 at 19:04:47 UTC, Steven Schveighoffer wrote:

>

On 9/23/21 2:20 PM, james.p.leblanc wrote:

>

Dear D-ers,

In attempting to cast JSONValues that hold arrays to "native"

>

How you really do this:

import std.algorithm : map;
auto z5 = jj["ba"] // get the JSONValue that is at the key "ba"
      .map!(v => v.get!bool) // map each value into a boolean
      .array // create an array out of the results;
assert z5 == [true, false, true];

(warning, untested)

-Steve

Steve,

Your thorough explanations have helped me understand quite much.
Thank you for your energy and patience in these forum contributions.

In fact, I had begun to think that "map", may be what was needed here
and I had made a few naive attempts ... but this did not go so well.
With your suggested solution, I believe I can make headway on this.

Thanks Again,
James

September 23, 2021

On Thursday, 23 September 2021 at 19:18:11 UTC, james.p.leblanc wrote:

>

On Thursday, 23 September 2021 at 19:04:47 UTC, Steven Schveighoffer wrote:

>

On 9/23/21 2:20 PM, james.p.leblanc wrote:

>

Dear D-ers,

Here comes a minor update (small rearrangement in mapping/array ordering)
for anyone who may be interested. With this small edit to the suggested code,
it works just fine! Here:


  auto z5 = jj["ba"].array.map!(v => v.get!bool);

  writeln("typeid(z5): ", typeid(z5), ", z5: ", z5);
  writeln("z5: ", z5);

Produces:

typeid(jj): std.json.JSONValue, jj: {"ba":[true,false,true],"d":[1.23399999999999999]}
typeid(z5): question.main.MapResult!(__lambda2, JSONValue[]).MapResult, z5: [true, false, true]
z5: [true, false, true]

Cheers,
James

September 24, 2021

On Thursday, 23 September 2021 at 20:32:36 UTC, james.p.leblanc wrote:

>

On Thursday, 23 September 2021 at 19:18:11 UTC, james.p.leblanc wrote:

>

On Thursday, 23 September 2021 at 19:04:47 UTC, Steven Schveighoffer wrote:

>

On 9/23/21 2:20 PM, james.p.leblanc wrote:

Produces:

typeid(jj): std.json.JSONValue, jj: {"ba":[true,false,true],"d":[1.23399999999999999]}
typeid(z5): question.main.MapResult!(__lambda2, JSONValue[]).MapResult, z5: [true, false, true]
z5: [true, false, true]

Sigh ... my suggested "minor edit" above produces a "map result" instead of
the desired native array ... here is the fix by appending ".array" at the
end (which is the suffix Steve originally offered). The following gives
the desired bool[] result.

import std.array;

   auto z5 = jj["ba"].array.map!(v => v.get!bool).array;
   writeln("typeid(z5): ", typeid(z5), ", z5: ", z5);
   writeln("z5: ", z5);

Regards,
James

September 24, 2021

On 9/24/21 12:58 AM, james.p.leblanc wrote:

>

On Thursday, 23 September 2021 at 20:32:36 UTC, james.p.leblanc wrote:

>

On Thursday, 23 September 2021 at 19:18:11 UTC, james.p.leblanc wrote:

>

On Thursday, 23 September 2021 at 19:04:47 UTC, Steven Schveighoffer wrote:

>

On 9/23/21 2:20 PM, james.p.leblanc wrote:

Produces:

typeid(jj): std.json.JSONValue, jj: {"ba":[true,false,true],"d":[1.23399999999999999]}
typeid(z5): question.main.MapResult!(__lambda2, JSONValue[]).MapResult, z5: [true, false, true]
z5: [true, false, true]

Sigh ... my suggested "minor edit" above produces a "map result" instead of
the desired native array ... here is the fix by appending ".array" at the
end (which is the suffix Steve originally offered).  The following gives
the desired bool[] result.

import std.array;

    auto z5 = jj["ba"].array.map!(v => v.get!bool).array;
    writeln("typeid(z5): ", typeid(z5), ", z5: ", z5);
    writeln("z5: ", z5);

At first I thought that was allocating 2 arrays, but I didn't realize JSONValue.array was an actual member! When I saw your original code, I assumed the .array call was a call to std.array.array, and that JSONValue was somehow usable as a range.

So yes, that's what I should have written.

I admit I have not used JSONValue. That array accessor is quite a poor name due to the vast prevalence of using std.array.array to make an array out of some range. When using vibe.d's JSON type, I usually do jsval[] to access the array portion.

Note, you may want to consider whether you actually need a concrete array, as using the map result is pretty much equivalent, yet doesn't allocate anything.

-STeve