Reputation: 841
I have an interface declared as
public interface ISomething<T> where T : class
and somewhere in the interface I have a member declared as
[JsonProperty("someProperty")]
[JsonConverter(typeof(ConcreteTypeConverter<List<T>>))]
List<T> SomePropertyList{ get; set; }
I get an error on ConcreteTypeConverter<List<T>>
saying it cannot use a type (T) as an argument. My ConcreteTypeConverter class takes a type T and returns a concrete implementation of T which is needed for JSON deserialization.The scenario here is that T can have about 20 different types.But I would like to avoid having 20 such interfaces - that's why I opted for a generic interface.
Usage would be something like
ISomething<SomeType> variable = new Something<SomeType>();
var list = variable.SomePropertyList;
where SomeType is the actual implementation of the T. Is there any way to use generics in such a situation?
My ConcreteConverterClass derives from JsonConverter (using Newtonsoft.Json):
public class ConcreteTypeConverter<TConcrete> : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return true;
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
serializer.Serialize(writer, value);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
return serializer.Deserialize<TConcrete>(reader);
}
}
My exact error is:
Attribute Argument cannot use type parameters
Upvotes: 3
Views: 2106
Reputation: 307
As it was told before, the generic parameters cannot appear in attribute declarations, I encountered the same problem and previous suggestions were not for me, I needed one SingleValueToCollectionConverter which could work like generic. And reflection helped me:
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;
}
And using like this (If Data will be Object in json, it will be converted to list):
public class BaseResponse<TData>
{
[JsonConverter(typeof(SingleValueToCollectionConverter))]
public List<TData> Data { get; set; }
}
I hope it can help somebody
Upvotes: 0
Reputation: 9790
Quoting:
[JsonConverter(typeof(ConcreteTypeConverter<List<T>>))]
List<T> SomePropertyList{ get; set; }
I get an error on ConcreteTypeConverter
Since the generic parameters cannot appear in attribute declarations, there is an advice for you from me:
public class ConcreteTypeConverter : JsonConverter
Removed the TConcrete
. Since the ReadJson
returns an object, the power of generics simply fades out.
Second,
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
return serializer.Deserialize<object>(reader);
}
Replaced TConcrete
with an object. If that is not enough for your code to work, try to workaround it. Your method returns an object
anyway.
Upvotes: 1
Reputation: 65451
You only need the ConcreteTypeConverter
if SomePropertyList
is not a concrete type, e.g.
[JsonProperty("someProperty")]
[JsonConverter(typeof(ConcreteTypeConverter<List<T>>))]
IList<T> SomePropertyList{ get; set; }
If this is not an issue, just change your property declaration to
[JsonProperty("someProperty")]
List<T> SomePropertyList{ get; set; }
Upvotes: 4
Reputation: 648
Please apply the attribute on the class implementing the interface.
[JsonProperty("someProperty")]
[JsonConverter(typeof(ConcreteTypeConverter<List<T>>))]
Upvotes: 0