Deserialize Json an access the result

I have this Json:

{
   "UpdatePack":"updatePacks\/1585654836.pack",
   "Updates":[
      {
         "Name":"MsgBoxEx",
         "version":"1.5.14.88",
         "ChangeLog":"BugFix: Form didn't resize correct.",
         "Hash":"5FB23ED83693A6D3147A0485CD13288315F77D3D37AAC0697E70B8F8C9AA0BB8"

},
      {
         "Name":"Utilities",
         "version":"2.5.1.58",
         "ChangeLog":"StringManagement updated.",
         "Hash":"05E6B3F521225C604662916F50A701E9783E13776DE4FCA27BE4B69705491AC5"

}

]
}

I have created 2 classes to be used to Deserialize it.

class UpdatesList
{
    public string Name { get; set; }
    public string Version { get; set; }
    public string ChangeLog { get; set; }
    public string Hash { get; set; }
}
class JsonObjectHolder
{
    public string UpdatePack { get; set; }
    //public Dictionary<int, MyData> { get; set; }
    public Dictionary<int, UpdatesList> Updates { get; set; }
}

But when I try to access the dictionary, I keep getting Unhandled Exception: System.NullReferenceException: Object reference not set to an instance of an object. at " Console.WriteLine(jsonTest.Dict.Count);"

Am I Deserializing it wrong, or do I need to do some thing else to access the result of the dictionary?

I'm new to both C# and Json.

I hope that some one could point me in the right direction on how to handle this.

I'm using Visual Studio 2019 latest update, and .net 4.8. Regards /LR

4 answers

  • answered 2020-03-31 10:08 Pavel Anikhouski

    You code doesn't work because 0 and 1 tokens just a properties, not the array items (you don't have square brackets [] around them). You can parse these values to desired structure manually using JObject

    var json = JObject.Parse(your_json_string);
    var dict = new Dictionary<int, UpdatesList>();
    foreach (var item in json.Properties())
    {
        if (item.Value.Type == JTokenType.Object)
        {
            var index = int.Parse(item.Name);
            var updateList = item.Value.ToObject<UpdatesList>();
            dict.Add(index, updateList);
        }
    }
    
    var holder = new JsonObjectHolder
    {
        UpdatePack = json["Updates"]?.Value<string>(),
        Dict = dict
    };
    

    Update: According to JSON changes from OP it might be deserialized even more simply

    var list = json["Updates"]?.ToObject<List<UpdatesList>>();
    
    var holder = new JsonObjectHolder
    {
        UpdatePack = json["UpdatePack"]?.Value<string>(),
        Dict = list.Select((updatesList, index) => new { updatesList, index })
                        .ToDictionary(x => x.index, x => x.updatesList)
    };
    

    The key point here is that Updates is an array of items, not the key-value collection. It can be transformed to Dictionary<int, UpdatesList> manually using ToDictionary method from System.Linq (or just use List<UpdatesList> as is)

  • answered 2020-03-31 10:22 Serkan Arslan

    your data and the class is not compatible. if you change the string like this it would work.

    change "Updates" to "UpdatePack" and add "Dict" around the dictionary items.

    {
       "UpdatePack":"updates\/4D1D7964D5B88E5867324F575B77D2FA.zip",
       "Dict":{
          "0":{
             "Name":"MsgBoxEx",
             "Version":"1.0.123.58",
             "ChangeLog":"Bugfix:Form didn't resize correct",
             "hash":"AA94556C0D2C8C73DD217974D252AF3311A5BF52819B06D179D17672F21049A6"
          },
          "1":{
             "Name":"Utilities",
             "Version":"1.5.321.87",
             "ChangeLog":"StringManagement updated",
             "hash":"2F561B02A49376E3679ACD5975E3790ABDFF09ECBADFA1E1858C7BA26E3FFCEF"
          }
       }
    }
    

  • answered 2020-03-31 10:52 Atharva

    The exception you're getting essentially means the value is being accessed before the object is initialized.

    A better, simpler and cleaner way to doing it is using NewtonSoft. (you can easily get it as a Nuget package)

    example:

    public class Account
    {
        public string Email { get; set; }
        public bool Active { get; set; }
        public DateTime CreatedDate { get; set; }
        public IList<string> Roles { get; set; }
    }
    

    and then usage:

    string json = @"{
      'Email': 'james@example.com',
      'Active': true,
      'CreatedDate': '2013-01-20T00:00:00Z',
      'Roles': [
        'User',
        'Admin'
      ]
    }";
    
    Account account = JsonConvert.DeserializeObject<Account>(json);
    
    Console.WriteLine(account.Email);
    

    Source: https://www.newtonsoft.com/json/help/html/DeserializeObject.htm

  • answered 2020-03-31 10:56 RoadRunner - MSFT

    I don't see why you need Dictionary<int, UpdatesList> Updates, when you can easily just use List<Update> Updates, since your updates are in a JSON array.

    I would model your classes like this:

    public class Update
    {
        public string Name { get; set; }
        public string Version { get; set; }
        public string ChangeLog { get; set; }
        public string Hash { get; set; }
    }
    
    public class RootObject
    {
        public string UpdatePack { get; set; }
        public List<Update> Updates { get; set; }
    }
    

    You can then deserialize with:

     JsonConvert.DeserializeObject<RootObject>(json);
    

    Try it out on dotnetfiddle.net

    Note: To convert JSON to C# classes, you can go to Edit -> Paste Special -> Paste JSON as Classes inside Visual Studio. Make sure you have copied the JSON to your clipboard before using it. You will get classes similar to above.