当属性类型包括泛型类型时,如何将泛型JsonConverter应用于泛型类的属性?

德米特里

我的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.ConverterJsonProperty.ItemConverter根据需要。

其次,您可以放弃通用转换器方法,而创建用于序列化过滤器和谓词的非通用转换器。您之所以这样做,是因为虽然在编写转换器时使用泛型是方便且易读的,但由于将所有必需的类型信息都传递到非泛型的读写方法中,因此并非绝对必要:

你的问题没有表现出最小重复的例子,你的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] 删除。

编辑于
0

我来说两句

0条评论
登录后参与评论

相关文章

来自分类Dev

如何将复杂的泛型方法应用于通配符泛型类型的类?

来自分类Dev

如何将具有泛型的函数类型应用于函数

来自分类Dev

访问泛型类型的属性

来自分类Dev

针对泛型类型的非泛型属性进行验证

来自分类Dev

如何使用泛型返回泛型类的类类型?

来自分类Dev

从泛型类型中获取属性类型

来自分类Dev

如何为泛型类指定泛型集合类型?

来自分类Dev

如何将泛型类扩展为泛型

来自分类Dev

将属性的类型传递给泛型类型

来自分类Dev

将泛型类型属性过滤为特定类型

来自分类Dev

如何将泛型类型转换为非泛型类型

来自分类Dev

如何将泛型类型指定为泛型(更高种类的类型)

来自分类Dev

获取属性类型并转换泛型

来自分类Dev

Swift泛型类型的属性和方法

来自分类Dev

基于泛型类型的可选属性

来自分类Dev

从扩展的泛型访问属性的类型

来自分类Dev

Kotlin从泛型类型获取属性

来自分类Dev

使用泛型参数化属性类型

来自分类Dev

基于泛型类型的可选属性

来自分类Dev

使用泛型类型的反射属性名称

来自分类Dev

具有泛型类型的属性

来自分类Dev

将接口用于Collection <>泛型类型

来自分类Dev

将接口用于Collection <>泛型类型

来自分类Dev

定义泛型类型时引用自己的属性

来自分类Dev

在泛型类型的泛型类上访问类型参数

来自分类Dev

如何使用类型的辅助类泛型

来自分类Dev

如何返回泛型类型的类

来自分类Dev

如何定义不是类的泛型类型?

来自分类Dev

如何将Swift协议与泛型方法和泛型类型一起使用