I am trying to create an abstraction layer for Json.NET deserialization using interfaces.
To achieve this I use custom JsonConverter
which works just fine, until interfaces are introduced.
Following exception is thrown:
Unhandled Exception: Newtonsoft.Json.JsonSerializationException: Error setting value to 'Items' on 'BatchList'. ---> System.InvalidCastException: Unable to cast object of type 'System.Collections.Generic.List1[BatchItems]' to type 'System.Collections.Generic.List`1[IBatchItems]
This is the setup to repro in a console app:
class Program
{
static void Main(string[] args)
{
var jsonBatch = @"{'items': [{'Id': 'name1','info': {'age': '20'}},{'Id': 'name2','info': {'age': '21'}}]}";
DeserializeAndPost(jsonBatch);
}
public static void DeserializeAndPost(string json)
{
IBatchList req;
req = JsonConvert.DeserializeObject<BatchList>(json);
Post(req);
}
public static void Post(IBatchList batchList)
{
Console.WriteLine(batchList.Items.FirstOrDefault().Id);
}
}
public interface IBatchList
{
List<IBatchItems> Items { get; set; }
}
public interface IBatchItems
{
string Id { get; set; }
JObject Info { get; set; }
}
[JsonObject(MemberSerialization.OptIn)]
public class BatchList : IBatchList
{
[JsonProperty(PropertyName = "Items", Required = Required.Always)]
[JsonConverter(typeof(SingleOrArrayConverter<BatchItems>))]
public List<IBatchItems> Items { get; set; }
}
[JsonObject]
public class BatchItems : IBatchItems
{
[JsonProperty(PropertyName = "Id", Required = Required.Always)]
public string Id { get; set; }
[JsonProperty(PropertyName = "Info", Required = Required.Always)]
public JObject Info { get; set; }
}
// JsonConverter
public 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 void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
List<T> list = (List<T>)value;
if (list.Count == 1)
{
value = list[0];
}
serializer.Serialize(writer, value);
}
public override bool CanWrite
{
get { return true; }
}
}
I expect the output to be deserialized JSON as I provide the type for the interface to be used for deserialization:
[JsonConverter(typeof(SingleOrArrayConverter<BatchItems>))]
to be used.
Instead, unhandled cast exception
is being thrown.
Note that if I use instead SingleOrArrayConverter<IBatchItems>
, I will get an exception
Newtonsoft.Json.JsonSerializationException: Could not create an instance of type
as the [JsonConverter(typeof(SingleOrArrayConverter<BatchItems>))]
is meant to provide concrete type for the following interface: public List<IBatchItems> Items { get; set; }
.