Abdullah Saleem
Abdullah Saleem

Reputation: 3811

Deserialize Json objects with Json.Net

I'm trying to deserialize an object with Json.net. I was able to do it successfully but its more of a hack so I'm looking for a better/proper way to do so.

{
"page":"admin",
"context":"id",
"records":{
            "rCount":6,
            "1":[
               {
                  "name":"Romeo",
                  "dob":"01\/01\/1970"
               },
               {
                  "name":"Harry",
                  "dob":"10\/10\/2012"
               },
               {
                  "name":"Lee",
                  "dob":"17\/10\/2012"
               }],
            "2":[
               {
                  "name":"Mark",
                  "dob":"01\/01\/1970"
               },
               {
                  "name":"Jack",
                  "dob":"10\/10\/2012"
               },
               {
                  "name":"Json",
                  "dob":"17\/10\/2012"
               }],

}}

this is the json string the issue is with the records object. if it doesn't have that rCount variable then it can be deserialized as a Dictionary but because of the rCount variable it can't be deserialized properly as a dictionary. What should be the proper way of deserialzing this object.

Here is my Solution:

class Program
{
static void Main(string[] args)
{
    var recordFile = JsonConvert.DeserializeObject<RecordFile>(Properties.Resources.data);
}

public class RecordFile
{
    public string Page { get; set; }
    public string Context { get; set; }
    public Records Records { get; set; }
}

public class Records
{
    public int RCount { get; set; }
    [JsonExtensionData]
    private Dictionary<string, object> _reocordList;

    public List<Record[]> RecordList
    {
        get
        {
            if (_reocordList != null && _reocordList.Count > 0)
            {
                return _reocordList.Values.Select(record => JsonConvert.DeserializeObject<Record[]>(record.ToString())).ToList();
            }
            return new List<Record[]>();
        }
    }

}

public class Record
{
    public string Name { get; set; }
    public string Dob { get; set; }
}
}

Upvotes: 3

Views: 1416

Answers (3)

user2160375
user2160375

Reputation:

You can use jObject to manually parse JSON:

public class RecordFile
{
    public string Page { get; set; }
    public string Context { get; set; }
    public Records Records { get; set; }
}

public class Records
{
    public int RCount { get; set; }
    public IDictionary<string, List<Record>> RecordsDictionary { get; set; } 
}


public class Record
{
    public string Name { get; set; }
    public string Dob { get; set; }
}

Then:

var jObject = JObject.Parse(\* your json *\);
var recordFile = new RecordFile
{
    Page = jObject.Value<string>("page"),
    Context = jObject.Value<string>("context"),
    Records = new Records
    {
        RCount = jObject["records"].Value<int>("rCount"),
        RecordsDictionary =
            jObject["records"].Children<JProperty>()
                              .Where(prop => prop.Name != "rCount")
                              .ToDictionary(prop => prop.Name),
                                            prop =>
                                            prop.Value.ToObject<List<Record>>())
    }

};

Of course it can be easily to handle cases, when property is not present.

Upvotes: 1

Brian Rogers
Brian Rogers

Reputation: 129657

The other answers posted so far should both work. For completeness, I'll show how you can use a custom JsonConverter to solve this and also simplify your model a bit.

Here is the code for the converter:

class RecordFileConverter : JsonConverter
{
    public override bool CanConvert(Type objectType)
    {
        return (objectType == typeof(RecordFile));
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        JObject jo = JObject.Load(reader);
        RecordFile rf = new RecordFile();
        rf.Page = (string)jo["page"];
        rf.Context = (string)jo["context"];
        JObject records = (JObject)jo["records"];
        rf.RecordCount = (int)records["rCount"];
        rf.Records = records.Properties()
                            .Where(p => p.Name != "rCount")
                            .Select(p => p.Value.ToObject<Record[]>())
                            .ToList();
        return rf;
    }

    public override bool CanWrite
    {
        get { return false; }
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        throw new NotImplementedException();
    }
}

To work with the converter, change your model classes as shown below. (Notice that the RecordFile class has a JsonConverter attribute to link the custom RecordFileConverter to it. Also notice the Records class is deleted, while the RCount and RecordList properties have been moved up to the RecordFile class and renamed.)

[JsonConverter(typeof(RecordFileConverter))]
public class RecordFile
{
    public string Page { get; set; }
    public string Context { get; set; }
    public int RecordCount { get; set; }
    public List<Record[]> Records { get; set; }
}

public class Record
{
    public string Name { get; set; }
    public string Dob { get; set; }
}

Then, deserialize as normal:

var recordFile = JsonConvert.DeserializeObject<RecordFile>(json);

Demo: https://dotnetfiddle.net/jz3zUT

Upvotes: 1

Andrew Whitaker
Andrew Whitaker

Reputation: 126042

I'm assuming you want to get rid of the Records class completely, ending up with something like this:

public class RecordFile
{
    public string Page { get; set; }
    public string Context { get; set; }
    public Dictionary<string, Record[]> Records { get; set; }
}

public class Record
{
    public string Name { get; set; }
    public string Dob { get; set; }
}

Since you don't care at all about the records.rCount property from the JSON, you could specify a new error handler to simply ignore the property:

var recordFile = JsonConvert.DeserializeObject<RecordFile>(
    jsonString, 
    new JsonSerializerSettings 
    {
        Error = (sender, args) =>
        {
            if (args.ErrorContext.Path == "records.rCount")
            {
                // Ignore the error
                args.ErrorContext.Handled = true;
            }
        }
    });

Now when an error is encountered with the property in question, the deserializer will just skip over it. Another option would be to write a custom converter, but that feels like overkill just to ignore one property.

Example: https://dotnetfiddle.net/3svPqk

Upvotes: 1

Related Questions