www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - How to parse a json node that contains children of different types?

reply Bagomot <bagomot gmail.com> writes:
I have a json like this:

```json
{
    "arguments":{
       "jvm":[
          {
             "rules":[
                {
                   "action":"allow",
                   "os":{
                      "name":"osx"
                   }
                }
             ],
             "value":[
                "-XstartOnFirstThread"
             ]
          },
          "-Djava.library.path=${natives_directory}"
       ]
    }
}
```
Here the "jvm" node is an array that has different data types for 
its elements (json object and string).

I am using [asdf](https://code.dlang.org/packages/asdf) library 
to deserialize json. I am trying to deserialize this json into a 
structure like this:
```d
struct Arguments{
      serdeKeys("jvm")
     Object[] jvm;
}
```
But there is a problem with different types. I understand that 
Object[] is not suitable here, but I don’t know how to do it.

I ask you to help me with this.
Jul 20
parent reply Mathias LANG <geod24 gmail.com> writes:
On Tuesday, 20 July 2021 at 21:18:12 UTC, Bagomot wrote:
 But there is a problem with different types. I understand that 
 Object[] is not suitable here, but I don’t know how to do it.

 I ask you to help me with this.
IIUC, the `arguments` are command line arguments passed to a Java program, and some of those might be passed to the JVM ? So you probably will end up with something like: ``` struct Arguments{ JVMArgs jvm; ProgramArgs program; } ``` Of course, the question is, what's `JVMArgs` ? Now, as you noticed, you can't represent an array of unrelated types in D (which makes sense). So what you can do is create a common type for it, by using an `union`. ``` union JVMArg { RuleValue ruleValue; string str; // Etc... } alias JVMArgs = JVMArg[]; ``` However, \<YOUR-JSON-LIBRARY> might not support it directly, or might support it through its own type (you will need a tagged union, and not just a simple union, as you need to know the type that the library read). But if you take a step back, I think you might find this solution is far from ideal. Having worked on a JSON library myself, I can tell you they are all implemented with a tagged union. And converting a tagged union to a tagged union is no improvement. Instead, I would recommend to just use the JSON directly. If you need to pass things around, you might want to deserialize it to a common format. For example, assuming your project is a process manager for Java program and you want to read a common set of arguments from a configuration file, you might want to just generate the strings: ``` auto myJSON = parseConfigFile("config.json"); string[] defaultJVMArgs = parseJVMArgs(myJSON); auto processMonitor = new ProcessMonitor(defaultJVMArgs); ``` TL;DR: If you want to use loosely typed data in a strongly typed language, you need to come up with a common type. That common type is usually either a discriminated union (which a JSON object is, essentially) or something that is domain-specific. Hope this helps. I had a quick look at asdf and couldn't see a support for tagged union, so you would probably need to provide your data structure with a `deserializeFromAsdf` method. If you want to do so, look into providing a wrapper to `SumType`, e.g.: ``` struct MyUnion (T...) { SumType!T data; alias data this; SerdeException deserializeFromAsdf(Asdf data) { /* Fill in the SumType */ } ``` But I would recommend just using the JSON object if you can.
Jul 20
next sibling parent Bagomot <bagomot gmail.com> writes:
On Wednesday, 21 July 2021 at 03:00:51 UTC, Mathias LANG wrote:
 TL;DR: If you want to use loosely typed data in a strongly 
 typed language, you need to come up with a common type. That 
 common type is usually either a discriminated union (which a 
 JSON object is, essentially) or something that is 
 domain-specific.

 Hope this helps. I had a quick look at asdf and couldn't see a 
 support for tagged union, so you would probably need to provide 
 your data structure with a `deserializeFromAsdf` method. If you 
 want to do so, look into providing a wrapper to `SumType`, e.g.:

 ```
 struct MyUnion (T...) {
     SumType!T data;
     alias data this;
     SerdeException deserializeFromAsdf(Asdf data) { /* Fill in 
 the SumType */ }
 ```

 But I would recommend just using the JSON object if you can.
Thank you! From your answer, I realized that in fact, I would rather do the parsing of these arguments myself. Before using asdf, I tried to do the same with the standard std.json. The problem is the same there. I agree that this is logical for D. I was also interested in SumType, I will try to do something with it for experience.
Jul 20
prev sibling parent Steven Schveighoffer <schveiguy gmail.com> writes:
On 7/20/21 11:00 PM, Mathias LANG wrote:

 But if you take a step back, I think you might find this solution is far 
 from ideal.
 Having worked on a JSON library myself, I can tell you they are all 
 implemented with a tagged union. And converting a tagged union to a 
 tagged union is no improvement.
Not [jsoniopipe](https://github.com/schveiguy/jsoniopipe). In that case, you could use a specialized type with a `fromJSON` static member to do whatever you wanted. -Steve
Jul 21