我的Json.NET有问题。当属性类型包括类本身的泛型类型参数时,我需要对一个也是泛型的类中的不同属性使用不同的泛型JsonConverters。
让我们拥有以下通用类,其中TTable
某些需要转换的属性使用参数:
public class ExpresssionDTO<TTable> : BaseDTO where TTable : class
{
[JsonProperty(ItemConverterType = typeof(PredicateSerializationConverter<>))]
public ICollection<Expression<Func<TTable, bool>>> Predicates { get; set; } = new List<Expression<Func<TTable, bool>>>();
[JsonConverter(converterType: typeof(FilterSerializationConverter<>))]
public Expression<Func<TTable, object>> Filter { get; set; } = null;
}
使用转换器:
public class PredicateSerializationConverter<TTable> : ExpressionSerializer<TTable, bool> where TTable : class
{
public PredicateSerializationConverter() :base()
{
}
}
public class FilterSerializationConverter<TTable> : ExpressionSerializer<TTable, object> where TTable : class
{
public FilterSerializationConverter() : base()
{
}
}
public class ExpressionSerializer<T, U> : JsonConverter where T : class
{
...
}
在我的合同解析器中,我有一个错误:
Cannot create an instance of WebFoundationClassesCore.ServiceClasses.Converters.PredicateSerializationConverter`1[TTable] because Type.ContainsGenericParameters is true.
这个问题也引起了DefaultContractResolver
。
有什么办法可以解决我的问题?
您想要做的是将通用参数TTable
用作自定义JSON转换器的通用参数,如下所示:
public class ExpresssionDTO<TTable> : BaseDTO where TTable : class
{
[JsonProperty(ItemConverterType = typeof(PredicateSerializationConverter<TTable>))] // Here
public ICollection<Expression<Func<TTable, bool>>> Predicates { get; set; } = new List<Expression<Func<TTable, bool>>>();
[JsonConverter(converterType: typeof(FilterSerializationConverter<TTable>))] // And here
public Expression<Func<TTable, object>> Filter { get; set; } = null;
}
但是您不能这样做,因为c#禁止在attribute中使用通用参数。似乎您希望,如果您为转换器指定一个开放的通用类型,并且父对象类型也是具有相同数量的通用参数的通用类型,那么Json.NET将通过插入父代的自动构造转换器。通用参数-在这里TTable
。不幸的是,这没有实现。
那么,您有什么选择呢?
首先,您可以创建一个从该构造继承的自定义合同解析器DefaultContractResolver
,并应用适当的具体泛型转换器。由于要将转换器应用于属性,因此需要覆盖DefaultContractResolver.CreateProperty
并设置JsonProperty.Converter
或JsonProperty.ItemConverter
根据需要。
其次,您可以放弃通用转换器方法,而创建用于序列化过滤器和谓词的非通用转换器。您之所以这样做,是因为虽然在编写转换器时使用泛型是方便且易读的,但由于将所有必需的类型信息都传递到非泛型的读写方法中,因此并非绝对必要:
JsonConverter.ReadJson(JsonReader, Type objectType, Object, JsonSerializer)
的objectType
表示反序列化,例如所需要的类型Expression<Func<TTable, bool>>
为PredicateSerializationConverter
。
JsonConverter.WriteJson(JsonWriter, Object value, JsonSerializer serializer)
。
这value
是要序列化的实际表达式,因此当然知道它的类型。
你的问题没有表现出最小重复的例子,你的ExpressionSerializer<T, U>
。如果您可以轻松地将其重写为非通用的,则应考虑这样做。如果没有,您可以采用装饰器模式,并将现有的通用转换器包装在一个装饰器中,该装饰器从objectType
或此类推导所需的通用参数,value
如下所示:
public class GenericFuncExpressionArgumentConverterDecorator : JsonConverter
{
readonly Type openGenericConverterType;
volatile Tuple<Type, JsonConverter> converterCache;
public GenericFuncExpressionArgumentConverterDecorator(Type openGenericConverterType)
{
if (openGenericConverterType == null)
throw new ArgumentNullException();
if (!openGenericConverterType.IsSubclassOf(typeof(JsonConverter)))
throw new ArgumentException(string.Format("{0} is not a JsonConvreter", GetType().Name));
if (!openGenericConverterType.IsGenericTypeDefinition)
throw new ArgumentException(string.Format("{0} is not an open generic type", GetType().Name));
this.openGenericConverterType = openGenericConverterType;
}
public override bool CanConvert(Type objectType) =>
throw new NotImplementedException(string.Format("{0} is intended to be applied via a JsonConverter or JsonProperty attribute", GetType().Name));
JsonConverter GetConverter(Type objectType)
{
var cache = converterCache;
if (cache != null && cache.Item1 == objectType)
return cache.Item2;
// Despite the documentation, Expression<T> is not actually sealed in .Net 5!
// https://github.com/dotnet/runtime/blob/master/src/libraries/System.Linq.Expressions/src/System/Linq/Expressions/LambdaExpression.cs#L174
var expressionType = objectType.BaseTypesAndSelf().Where(t => t.IsGenericType && t.GetGenericTypeDefinition() == typeof(Expression<>)).FirstOrDefault();
if (expressionType == null)
throw new JsonSerializationException(string.Format("Invalid expression type {0}", objectType));
var delegateType = objectType.GetGenericArguments().Single();
if (!delegateType.IsGenericType || delegateType.GetGenericTypeDefinition() != typeof(Func<,>))
throw new JsonSerializationException(string.Format("Invalid delegate type {0}", delegateType));
var argType = delegateType.GetGenericArguments()[0];
var converterType = openGenericConverterType.MakeGenericType(new [] { argType });
var converter = (JsonConverter)Activator.CreateInstance(converterType);
converterCache = Tuple.Create(objectType, converter);
return converter;
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) =>
GetConverter(objectType).ReadJson(reader, objectType, existingValue, serializer);
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) =>
GetConverter(value.GetType()).WriteJson(writer, value, serializer);
}
public static class TypeExtensions
{
public static IEnumerable<Type> BaseTypesAndSelf(this Type type)
{
while (type != null)
{
yield return type;
type = type.BaseType;
}
}
}
然后按如下所示将其应用于您的模型,将开放的通用转换器类型作为转换器参数传递:
public class ExpresssionDTO<TTable> : BaseDTO where TTable : class
{
[JsonProperty(ItemConverterType = typeof(GenericFuncExpressionArgumentConverterDecorator), ItemConverterParameters = new object [] { typeof(PredicateSerializationConverter<>) })]
public ICollection<Expression<Func<TTable, bool>>> Predicates { get; set; } = new List<Expression<Func<TTable, bool>>>();
[JsonConverter(typeof(GenericFuncExpressionArgumentConverterDecorator), new object [] { typeof(FilterSerializationConverter<>) })]
public Expression<Func<TTable, object>> Filter { get; set; } = null;
}
演示在这里摆弄。
本文收集自互联网,转载请注明来源。
如有侵权,请联系[email protected] 删除。
我来说两句