I am using an API that returns a json object that I need to deserialize. My problem is that one of the members of those object is sometimes an empty array ("[]") and sometimes a dictionary ("{"1":{...}, "2":{...}}"). I want to deserialize it into either an array or a dictionary, since I don't car about the IDs, I just want a list of all the objects. Here is how I deserialize the object:
var response = JsonConvert.DeserializeObject<Response>(json);
And here is the definition of the Response class:
public class Response
{
[JsonProperty(PropertyName = "variations")]
public Dictionary<int, Variation> Variations { get; set; }
}
It works well when the Response contains a dictionary in it's variations field, but it fails when it contains an empty array. I'm getting an error from Newtonsoft saying that an array cannot be deserialized into a dictionary. If I define the Variations property as an array, it works for empty arrays, but it fails when it is a dictionary. What could I do to either deserialize correctly both possible values, or to ignore empty arrays and set Variations to null when it's an array instead of failing.
Thanks.
NET objects (deserialize) A common way to deserialize JSON is to first create a class with properties and fields that represent one or more of the JSON properties. Then, to deserialize from a string or a file, call the JsonSerializer. Deserialize method.
Case insensitive deserialization: By default, Newtonsoft. Json does case insensitive property name matching during deserialization whereas System. Text.
DeserializeObject Method. Deserializes the JSON to a . NET object.
SerializeObject Method (Object, Type, JsonSerializerSettings) Serializes the specified object to a JSON string using a type, formatting and JsonSerializerSettings.
Here an alternative solution using JsonConverter
public class Response
{
[JsonProperty(PropertyName = "variations")]
[JsonConverter(typeof(EmptyArrayOrDictionaryConverter))]
public Dictionary<int, Variation> Variations { get; set; }
}
public class EmptyArrayOrDictionaryConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return objectType.IsAssignableFrom(typeof(Dictionary<string, object>));
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
JToken token = JToken.Load(reader);
if (token.Type == JTokenType.Object)
{
return token.ToObject(objectType, serializer);
}
else if (token.Type == JTokenType.Array)
{
if (!token.HasValues)
{
// create empty dictionary
return Activator.CreateInstance(objectType);
}
}
throw new JsonSerializationException("Object or empty array expected");
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
serializer.Serialize(writer, value);
}
}
Here is a variation (sorry for the pun) on Carl's example. I had a similar need, but instead of returning a dictionary, I needed an array. The API I am using says it returns an array. However, in the case where there is only one item in the result, it is returned as an object instead!
public class VarationsContainer
{
[JsonIgnore]
public Varation[] Varations
{
get
{
return ParseObjectToArray<Variation>(VariationObject);
}
}
[JsonProperty(PropertyName = "varations")]
public object VarationsObject { get; set; }
protected T[] ParseObjectToArray<T>(object ambiguousObject)
{
var json = ambiguousObject.ToString();
if (String.IsNullOrWhiteSpace(json))
{
return new T[0]; // Could return null here instead.
}
else if (json.TrimStart().StartsWith("["))
{
return JsonConvert.DeserializeObject<T[]>(json);
}
else
{
return new T[1] { JsonConvert.DeserializeObject<T>(json) };
}
}
}
I hope this is useful to some other sad API consumer.
Here is the solution I used :
public Dictionary<int, Variation> Variations
{
get
{
var json = this.VariationsJson.ToString();
if (json.RemoveWhiteSpace() == EmptyJsonArray)
{
return new Dictionary<int, Variation>();
}
else
{
return JsonConvert.DeserializeObject<Dictionary<int, Variation>>(json);
}
}
}
[JsonProperty(PropertyName = "variations")]
public object VariationsJson { get; set; }
Basically, the variations are first deserialized in a basic object. When I want to read the value, I check if the object is an empty array, and if so I return an empty dictionary. If the object is a good dictionary, I deserialize it and return it.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With