Goozo
Goozo

Reputation: 956

Deserializing with a json file with inner classes with JSON.NET C#

I have the following structure in my json file

  "missionName": "missionname",
  "thumb_image": "pics/mission-info.png",
   "uplinkPage": [
    {
        "RPC": {
            "name": "RPC",
            "rpc": "",
            "args": ""
        },
        "EXE": {
            "name": "app1",
            "prog": "",
            "args": ""
        },
        "VM": {
            "name": "VM",
            "name": "",
            "args": ""
        },
        "JAR": {
            "name": "JAR",
            "prog": "",
            "args": ""
        },
        "link": {
            "name": "somelink",
            "url": ""
        }
    }
],

for this I have the following class

 public class EXE
{
   public string name { get; set; }
   public string prog { get; set; }
   public string args { get; set; }
}

public class RPC
{
    public string name { get; set; }
    public string rpc { get; set; }
    public string args { get; set; }
}

public class VM
{
    public string name { get; set; }
    public string args { get; set; }
}

public class JAR
{
    public string name { get; set; }
    public string prog { get; set; }
    public string args { get; set; }
}

public class Link
{
    public string name { get; set; }
    public string url { get; set; }
}

 public class UplinkPage
{
    public VM[] vmList { get; set; }
    public EXE[] exeList { get; set; }
    public RPC[] rpcList { get; set; }
    public JAR[] jarList { get; set; }
    public Link[] linkList { get; set; }
}

 public class Rootobject
{
    public string missionName { get; set; }
    public string thumb_image { get; set; }     
    public Uplinkpage[] uplinkPage { get; set; }        
}

the uplinkPage section can have one or many of each of the EXE, RPC, VM .. sections. I have tried to add multiply sections like this

"EXE": {
            "1": {
                "name": "app1",
                "data-prog": "",
                "data-args": ""
            },
            "2": { 
                "name": "app2",
            "data-prog": "",
            "data-args": ""
            }
        }

when I do the deserialization as so

Rootobject page = JsonConvert.DeserializeObject<Rootobject>(File.ReadAllText("mission1.json"));

I get an object and I can read everything except if I have multiply values of the same type, I will only get one of them. If I declare sections as array

public EXE[] exeList { get; set; }

I will get the last one and if as

Dictionary<string,EXE> exeList {get; set;}

I will get the first one. I have noticed that when I use the dictionary the type of the EXE changes to EXE1.

So I was wondering what am I doing wrong? am I mission some thing here?

cheers, es

Upvotes: 1

Views: 2213

Answers (1)

dbc
dbc

Reputation: 117046

Whenever you have a JSON property that might be duplicated, it will be easiest to represent the value of that property as JSON array, rather than multiple properties with duplicated names. I.e. instead of

{
    "EXE" : {"id":1},
    "RPC" : {"name":"a"},        
    "EXE" : {"id":2},
}

You want to do:

{
    "EXE" : [{"id":1}, {"id":2}],
    "RPC" : [{"name":"a"}]        
}

Similarly, in the VM class, name appears multiple times, so name should be an array as well:

public class VM
{
    public string [] name { get; set; }
    public string args { get; set; }
}

(It's not impossible to deserialize duplicated property names, just difficult, and requires a custom converter. See How to deserialize JSON with duplicate property names in the same object. Since you control your JSON format, I'd recommend avoiding this. Using a nested object with indexed properties as you propose in your question is also an option; it is less difficult but still requires custom conversion. See How to parse this JSON using Newton Soft for nested object. But using a JSON array is easiest.)

Next, you need to tell Json.NET how to map c# property names to JSON property names when they are inconsistent. For instance, in your JSON you have a property "EXE" but in c# the property name is public EXE[] exeList { get; set; }. You can either rename the JSON properties, rename the c# properties, or make the mapping using [JsonProperty]:

public class UplinkPage
{
    [JsonProperty("VM")]
    public VM[] vmList { get; set; }

    [JsonProperty("EXE")]
    public EXE[] exeList { get; set; }

    [JsonProperty("RPC")]
    public RPC[] rpcList { get; set; }

    [JsonProperty("JAR")]
    public JAR[] jarList { get; set; }

    [JsonProperty("link")]
    public Link[] linkList { get; set; }
}

I notice also that your EXE object sometimes has a "data-prog" property, and sometimes just a "prog". You should standardize on one.

Thus your JSON should look like:

{
  "missionName": "missionname",
  "thumb_image": "pics/mission-info.png",
  "uplinkPage": [
    {
      "RPC": [
        {
          "name": "RPC",
          "rpc": "",
          "args": ""
        }
      ],
      "EXE": [
        {
          "name": "app1",
          "prog": "prog1",
          "args": "args1"
        },
        {
          "name": "app2",
          "prog": "prog2",
          "args": "args2"
        }
      ],
      "VM": [
        {
          "name": [
            "VM",
            ""
          ],
          "args": ""
        }
      ],
      "JAR": [
        {
          "name": "JAR",
          "prog": "",
          "args": ""
        }
      ],
      "link": [
        {
          "name": "somelink",
          "url": ""
        }
      ]
    }
  ]
}

And your classes should look like:

public class EXE
{
    public string name { get; set; }
    public string prog { get; set; }
    public string args { get; set; }
}

public class RPC
{
    public string name { get; set; }
    public string rpc { get; set; }
    public string args { get; set; }
}

public class VM
{
    public string [] name { get; set; }
    public string args { get; set; }
}

public class JAR
{
    public string name { get; set; }
    public string prog { get; set; }
    public string args { get; set; }
}

public class Link
{
    public string name { get; set; }
    public string url { get; set; }
}

public class UplinkPage
{
    [JsonProperty("VM")]
    public VM[] vmList { get; set; }

    [JsonProperty("EXE")]
    public EXE[] exeList { get; set; }

    [JsonProperty("RPC")]
    public RPC[] rpcList { get; set; }

    [JsonProperty("JAR")]
    public JAR[] jarList { get; set; }

    [JsonProperty("link")]
    public Link[] linkList { get; set; }
}

public class Rootobject
{
    public string missionName { get; set; }
    public string thumb_image { get; set; }
    public UplinkPage[] uplinkPage { get; set; }
}

prototype fiddle.

Upvotes: 1

Related Questions