Reputation: 43
I'm having a little trouble trying to interpret the response from an API server in C#.
This is the API response we get in pure JSON;
[
{
"response" : { "test" : "Value" }
},
{
"response" : []
}
]
How can I get Newtonsoft JSON to handle a response like this?
At the present moment I have the "response" object as a Model, as soon as the content is processed by Newtonsoft using JsonConvert.DeserialiseObject
I receive the below error.
Newtonsoft.Json.JsonSerializationException: Cannot deserialize the current JSON array (e.g. [1,2,3]) into type '(Redacted)' because the type requires a JSON object (e.g. {"name":"value"}) to deserialize correctly. To fix this error either change the JSON to a JSON object (e.g. {"name":"value"}) or change the deserialized type to an array or a type that implements a collection interface (e.g. ICollection, IList) like List that can be deserialized from a JSON array. JsonArrayAttribute can also be added to the type to force it to deserialize from a JSON array.
Upvotes: 0
Views: 1550
Reputation:
You need to inspect the responses one by one and handle them differently based on what they are.
You can load up the JSON dynamically, then deserialise them based on what got loaded.
i.e. Objects deserialised as objects, and arrays deserialised as arrays.
Here's an example of how you can achieve that:
.Net Fiddle of this working.
using System;
using Newtonsoft.Json.Linq;
public class Program
{
public static void Main()
{
var json = "[{ \"response\" : { \"test\" : \"Value1\" } }, { \"response\" : [ { \"test\" : \"Value2\" }, { \"test\" : \"Value3\" }] }]";
var responseContainers = JArray.Parse(json);
foreach(var responseContainer in responseContainers)
{
var response = responseContainer.Value<JToken>("response");
if(response.Type == JTokenType.Object)
{
var data = response.ToObject<Data>();
Console.WriteLine("data: " + data.test);
}
else if(response.Type == JTokenType.Array)
{
var dataJArray = response.ToObject<JArray>();
foreach(var dataJToken in dataJArray)
{
var data = dataJToken.ToObject<Data>();
Console.WriteLine("datas: " + data.test);
}
}
}
}
}
public class Data
{
public string test { get;set; }
}
Output:
data: Value1
datas: Value2
datas: Value3
Alternatively, you could use the SingleOrArrayConverter
from this question.
using System;
using System.Collections.Generic;
using Newtonsoft.Json.Linq;
using Newtonsoft.Json;
public class Program
{
public static void Main()
{
var json = "[{ \"response\" : { \"test\" : \"Value1\" } }, { \"response\" : [ { \"test\" : \"Value2\" }, { \"test\" : \"Value3\" }] }]";
var items = JsonConvert.DeserializeObject<List<Item>>(json);
foreach(var item in items)
{
var responses = item.response;
Console.WriteLine("ResponseCount: " + responses.Count);
foreach(var response in responses)
{
Console.WriteLine("response: " + response.test);
}
}
}
}
public class Item
{
[JsonConverter(typeof(SingleOrArrayConverter<Data>))]
public List<Data> response { get;set; }
}
public class Data
{
public string test { get;set; }
}
class SingleOrArrayConverter<T> : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return (objectType == typeof(List<T>));
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
JToken token = JToken.Load(reader);
if (token.Type == JTokenType.Array)
{
return token.ToObject<List<T>>();
}
return new List<T> { token.ToObject<T>() };
}
public override bool CanWrite
{
get { return false; }
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
Output:
ResponseCount: 1
response: Value1
ResponseCount: 2
response: Value2
response: Value3
Upvotes: 1
Reputation: 43
I've applied a fix to deal with this specific issue.
By adding .Replace("\"response\":[]","\"response\":{}")
to the string being interpreted by the JsonConverter. This will only fix these specific occurrences within the retrieved code.
Anyone have a better answer that isn't some hacked-together bandaid fix like this?
Upvotes: 0