0

Some time ago I needed a custom generic json-converter which automatically would convert object to collection if needed. My solution was:

public class SingleValueCollectionConverter<T> : JsonConverter
{
    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        throw new NotImplementedException();
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        return reader.TokenType == JsonToken.StartArray ? serializer.Deserialize(reader, objectType) : new List<T> { (T)serializer.Deserialize(reader, typeof(T)) };
    }

    public override bool CanConvert(Type objectType)
    {
        return true;
    }
}

It worked fine until I've got the situation when I needed to use it for the property with generic type. I've got an error for using it like this:

[JsonConverter(typeof(SingleValueCollectionConverter<TData>))]
public List<TData> Data { get; set; }

It turned out that generic parameters cannot appear in attribute declarations. I found this pull-request to newtonsoftJsonConverter - https://github.com/JamesNK/Newtonsoft.Json/issues/1332 where the decision was suggested but it was not approved by the author. So, I created non-generic converter which works universally like generic with the help of reflection:

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

      public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
      {
         foreach (var prop in objectType.GetProperties())
         {
             var type = prop.PropertyType;

             if (!type.IsClass)
                 continue;

             var destination = Activator.CreateInstance(objectType);
             var result = reader.TokenType == JsonToken.StartArray 
                 ? serializer.Deserialize(reader, objectType) 
                 : new List<object> { serializer.Deserialize(reader, type) };

             return Mapper.Map(result, destination, result.GetType(), destination.GetType());
            }

            return null;
        }

        public override bool CanConvert(Type objectType)
        {
            return true;
        }
    }

Using like this:

 [JsonConverter(typeof(SingleValueCollectionConverter))]
 public List<TData> Data { get; set; }

It works fine, but maybe somebody has better decision. I'll be appreciated for any suggestions.

Artem Bakhmat
  • 187
  • 2
  • 9
  • Have you seen CustomContractResolver comment in the pull request on the github? – Access Denied Sep 19 '18 at 08:20
  • Yes, I saw it, but didn't understand how to use it. Besides there are several changes in many files. I'll be appreciated for helping with using CustomContractResolver – Artem Bakhmat Sep 19 '18 at 08:24
  • According to the comment it should work without changes. All you have to do is implement this resolver and use it like it's described in the following thread https://stackoverflow.com/a/27333955/1099716 – Access Denied Sep 19 '18 at 09:06
  • I've read the thread, created CustomContractResolver and I'm intending using it like this: var response = await ExecuteAsync(resource, method, data); var settings = new JsonSerializerSettings {ContractResolver = new CustomContractResolver()}; var result = JsonConvert.DeserializeObject(response.Content, settings); But what about error for sending generic type as parameter to json-converter: [JsonConverter(typeof(SingleValueCollectionConverter))] public List Data { get; set; } Error: Attribute argument cannot use type parameters. What cannot I understand? – Artem Bakhmat Sep 19 '18 at 09:50
  • Why don't you ask author of this pull request on the github? I'm pretty sure it's not a big deal for him to answer your question. – Access Denied Sep 19 '18 at 09:53
  • Asked, waiting response – Artem Bakhmat Sep 19 '18 at 10:21

0 Answers0