Shubhan
Shubhan

Reputation: 1022

Deserialise dynamic json types

I want to deserialize json which returns different data for different parameters. Mostly I get:

 {"posts": [{ "id" : 1, "name":"post1" },{ "id" : 1, "name":"post1" }]}

But sometimes the data returned is

{"posts": false}

I want to deserialize this as the following class

public class GetReaderResponse
{
   public IEnumerable<ReaderPost> posts {get; set;}
}

public class ReaderPost
{
   public int id {get; set;}
   public string name{get; set;}
}

I am using C#,json.net but not able to do this correctly.

Newtonsoft.Json.JsonConvert.DeserializeObject<GetReaderResponse>(dataString);

Upvotes: 1

Views: 95

Answers (4)

Andrew Whitaker
Andrew Whitaker

Reputation: 126052

You could build a custom converter, but an easy way to handle this would be to write an error handler that detects errors with the posts property:

var settings = new JsonSerializerSettings();

settings.Error += (sender, args) =>
{
    if (string.Equals("posts", args.ErrorContext.Path, StringComparison.OrdinalIgnoreCase))
    {
        var currentObject = args.CurrentObject as GetReaderResponse;

        currentObject.posts = Enumerable.Empty<ReaderPost>();

        args.ErrorContext.Handled = true;
    }
};

GetReaderResponse resp = 
    JsonConvert.DeserializeObject<GetReaderResponse>(json, settings);

This sets posts to Enumerable.Empty<ReaderPost>. This is still a little unsatisfying because if any error occurs, the property will be set. You could build a full custom converter to do this as a more complete solution.

Here's a converter that will take care of this:

public class PostsConverter : JsonConverter
{
    public override object ReadJson(
        JsonReader reader,
        Type objectType,
        object existingValue,
        JsonSerializer serializer)
    {
        JToken val = JValue.ReadFrom(reader);
        object result = null;

        if (val.Type == JTokenType.Array)
        {
            result = val.ToObject<IEnumerable<ReaderPost>>();
        }
        else if (val.Type == JTokenType.Boolean)
        {

            result = Enumerable.Empty<ReaderPost>();
        }

        return result;
    }

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

    public override bool CanConvert (Type type)
    {
        return typeof(IEnumerable<ReaderPost>).IsAssignableFrom(type);
    }

    public override bool CanRead
    {
        get { return true; }
    }
}

Usage:

var settings = new JsonSerializerSettings();

settings.Converters = new [] { new PostsConverter() };

GetReaderResponse resp = 
    JsonConvert.DeserializeObject<GetReaderResponse>(json, settings);

Example: https://dotnetfiddle.net/i9CXwp

Upvotes: 2

Shubhan
Shubhan

Reputation: 1022

After reading @Ilija's comment I think I might have found a answer. I did not want not use string literals so I modified my class GetReaderResponse to look like below:

public class GetReaderResponse
{
    public dynamic posts {get; set;}
    public IEnumerable<ReaderPost> Posts
    {
        get
        {
             if (posts is bool )
               return new ReaderPost[0];

           return posts.ToObject<IEnumerable<ReaderPost>>();            
        }
    }
}

Does this sound fine or does it look messy?

Upvotes: 0

Ilija Dimov
Ilija Dimov

Reputation: 5299

By using JSON.NETs built in LINQ to JSON, you can try someting like this:

JObject jObject = JObject.Parse(json);
GetReaderResponse response = new GetReaderResponse();
if (jObject["posts"] is JArray)
    response = jObject.ToObject<GetReaderResponse>();

// Do something with the response object.

where json variable is the json string you need to deserialize.

Upvotes: 1

Z .
Z .

Reputation: 12837

try this:

public class GetReaderResponse
{
    public bool posts { get; set; }
    public ReaderPost[] post { get; set; }
}

Upvotes: 0

Related Questions