The value for images
seems to be either:
- An object corresponding to a dictionary of lists of image data for arbitrary colors, here
"WHITE"
.
- An empty array when there are no colors values.
Since the value of images
is sometimes an array and sometimes an object, you will need to deserialize it using an appropriate custom JsonConverter<T>
.
Specifically, define the following data model:
public class TestClass
{
public List<Variant> variants { get; set; } = new ();
}
public class Variant
{
public string name { get; set; }
public string description { get; set; }
[JsonConverter(typeof(JsonSingleOrEmptyArrayConverter<Dictionary<string, List<Image>>>))]
public Dictionary<string, List<Image>> images { get; set; } = new ();
}
public class Image
{
public string id { get; set; }
public string product_id { get; set; }
}
Where JsonSingleOrEmptyArrayConverter<T>
is copied verbatim from this answer to Cannot Deserialize the Current JSON Object (Empty Array):
public class JsonSingleOrEmptyArrayConverter<T> : JsonConverter where T : class
{
// Copied from this answer https://stackoverflow.com/a/53219877
// To https://stackoverflow.com/questions/53219668/cannot-deserialize-the-current-json-object-empty-array
public override bool CanConvert(Type objectType)
{
return typeof(T).IsAssignableFrom(objectType);
}
public override bool CanWrite { get { return false; } }
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
var contract = serializer.ContractResolver.ResolveContract(objectType);
// Allow for dictionary contracts as well as objects contracts, since both are represented by
// an unordered set of name/value pairs that begins with { (left brace) and ends with } (right brace).
if (!(contract is Newtonsoft.Json.Serialization.JsonObjectContract
|| contract is Newtonsoft.Json.Serialization.JsonDictionaryContract))
{
throw new JsonSerializationException(string.Format("Unsupported objectType {0} at {1}.", objectType, reader.Path));
}
switch (reader.SkipComments().TokenType)
{
case JsonToken.StartArray:
{
int count = 0;
while (reader.Read())
{
switch (reader.TokenType)
{
case JsonToken.Comment:
break;
case JsonToken.EndArray:
// You might want to allocate an empty object here if existingValue is null
// If so, do
// return existingValue ?? contract.DefaultCreator();
return existingValue;
default:
{
count++;
if (count > 1)
throw new JsonSerializationException(string.Format("Too many objects at path {0}.", reader.Path));
existingValue = existingValue ?? contract.DefaultCreator();
serializer.Populate(reader, existingValue);
}
break;
}
}
// Should not come here.
throw new JsonSerializationException(string.Format("Unclosed array at path {0}.", reader.Path));
}
case JsonToken.Null:
return null;
case JsonToken.StartObject:
existingValue = existingValue ?? contract.DefaultCreator();
serializer.Populate(reader, existingValue);
return existingValue;
default:
throw new InvalidOperationException("Unexpected token type " + reader.TokenType.ToString());
}
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
public static partial class JsonExtensions
{
public static JsonReader SkipComments(this JsonReader reader)
{
while (reader.TokenType == JsonToken.Comment && reader.Read())
;
return reader;
}
}
And now you will be able to deserialize your JSON by simply doing:
var root = JsonConvert.DeserializeObject<TestClass>(json);
Demo fiddle here.