如何为属性类型的通用参数指定自定义json转换器

我想为一个属性分层多个JSON转换器,因为我需要为该属性的通用内部类型指定一个转换器。

用例:

public class A {
    public Option<DateTime> Time { get; set; }
}

我有一个OptionJsonConverter,可以对任何Option进行反序列化,并且我想使用this answer中的DateFormatConverter为此属性指定一个自定义日期格式字符串。 / p>

编写自定义转换器将是一个解决方案,但这并不理想,因为我将进行大量的代码重复。

我可以使用可为null的类型,但是我已经将代码库提交给Option,以避免进行空比较,并且将来无论如何其他类型都可能出现此问题。

jhiyueojvkcjksw 回答:如何为属性类型的通用参数指定自定义json转换器

ConvertersReadJson调用期间,转换器可以修改序列化程序的WriteJson属性,并且在嵌套序列化和反序列化过程中尊重新集合的内容。

这样,我们可以制作一个将指定的转换器临时添加到Converters属性的转换器,如下所示:

public abstract class CascadeJsonConverterBase : JsonConverter
{
    private readonly JsonConverter[] augmentConverters;

    protected CascadeJsonConverterBase() : this(new JsonConverter[0]) { }

    // this constructor is intended for use with JsonConverterAttribute
    protected CascadeJsonConverterBase(object[] augmentConverters)
        : this(augmentConverters.Select(FromAttributeData).ToArray())
    { }

    protected CascadeJsonConverterBase(JsonConverter[] augmentConverters)
    {
        this.augmentConverters = augmentConverters;
    }

    protected static JsonConverter FromAttributeData(object augmentConverterObj)
    {
        if (!(augmentConverterObj is object[] augmentConverter))
        {
            throw new ArgumentException($"Each augment converter data should be an object array",nameof(augmentConverters));
        }

        if (augmentConverter.Length < 1)
        {
            throw new ArgumentException($"Augment converter data should include at least one item",nameof(augmentConverters));
        }

        object augmentConverterType = augmentConverter[0];
        if (!(augmentConverterType is Type convType))
        {
            throw new ArgumentException($"Augment converter data should start with its type",nameof(augmentConverters));
        }

        if (!typeof(JsonConverter).IsAssignableFrom(convType))
        {
            throw new ArgumentException($"Augment converter type should inherit from JsonConverter abstract type",nameof(augmentConverters));
        }

        object converter = Activator.CreateInstance(convType,augmentConverter.SubArray(1,augmentConverter.Length - 1));
        return (JsonConverter)converter;
    }

    protected abstract void WriteJsonInner(JsonWriter writer,object value,JsonSerializer serializer);

    protected abstract object ReadJsonInner(JsonReader reader,Type objectType,object existingValue,JsonSerializer serializer);

    public override void WriteJson(JsonWriter writer,JsonSerializer serializer)
    {
        using (AugmentedConverterScope(serializer))
        {
            WriteJsonInner(writer,value,serializer);
        }
    }

    public override object ReadJson(JsonReader reader,JsonSerializer serializer)
    {
        using (AugmentedConverterScope(serializer))
        {
            return ReadJsonInner(reader,objectType,existingValue,serializer);
        }
    }

    private AugmentedConverterScopeMgr AugmentedConverterScope(JsonSerializer serializer)
    {
        // add augmented converters
        for (int i = augmentConverters.Length - 1; i >= 0; i--)
        {
            serializer.Converters.Insert(0,augmentConverters[i]);
        }

        return new AugmentedConverterScopeMgr(serializer,augmentConverters.Length);
    }

    private class AugmentedConverterScopeMgr : IDisposable
    {
        private readonly JsonSerializer serializer;
        private readonly int converterCount;

        public AugmentedConverterScopeMgr(JsonSerializer serializer,int converterCount)
        {
            this.serializer = serializer;
            this.converterCount = converterCount;
        }

        public void Dispose()
        {
            // remove augmented converters
            for (int i = 0; i < converterCount; i++)
            {
                serializer.Converters.RemoveAt(0);
            }
        }
    }
}

然后创建一个转换器,该转换器包装另一个转换器的逻辑,如下所示:

public class CascadeJsonConverter : CascadeJsonConverterBase
{
    private readonly JsonConverter wrappedConverter;

    public CascadeJsonConverter(Type wrappedConverterType,object[] wrappedConvConstructorArgs,object[] augmentConverters)
        : this(CreateConverter(wrappedConverterType,wrappedConvConstructorArgs),augmentConverters.Select(FromAttributeData).ToArray())
    { }

    public CascadeJsonConverter(JsonConverter wrappedConverter,JsonConverter[] augmentConverters)
        : base(augmentConverters)
    {
        this.wrappedConverter = wrappedConverter;
    }

    private static JsonConverter CreateConverter(Type converterType,object[] convConstructorArgs)
    {
        if (!typeof(JsonConverter).IsAssignableFrom(converterType))
        {
            throw new ArgumentException($"Converter type should inherit from JsonConverter abstract type",nameof(converterType));
        }

        return (JsonConverter) Activator.CreateInstance(converterType,convConstructorArgs);
    }

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

    protected override void WriteJsonInner(JsonWriter writer,JsonSerializer serializer)
    {
        wrappedConverter.WriteJson(writer,serializer);
    }

    protected override object ReadJsonInner(JsonReader reader,JsonSerializer serializer)
    {
        return wrappedConverter.ReadJson(reader,serializer);
    }
}

然后可以用来完成上述目标

public class A {
    [JsonConverter(typeof(CascadeJsonConverter),// cascading converter
                   typeof(OptionJsonConverter),new object[0],// converter definition for the top-level type of the property
                   new object[] { // collection of converter definitions to use while deserializing the contents of the property
                       new object[] { typeof(DateFormatConverter),"yyyy'-'MM'-'dd'T'mm':'HH':'FF.ssK" }
                   })]
    public Option<DateTime> Time { get; set; }
}

这样,您不仅可以为通用字段使用不同的控制器,而且还可以在类需要为属性类的某些子属性更改转换器的情况下使用。整洁:)

对此的一个警告是,顶级转换器必须使用serializerReadJson方法中的WriteJson参数来读取和写入内部值,而不是使用{{1} }和JToken.Load(reader).ToObject<T>()。否则,将使用未配置的序列化器读取和写入内部值。

如果有更好的方法来完成相同的任务,我真的很感谢您分享它!

本文链接:https://www.f2er.com/3136073.html

大家都在问