如何将自定义JsonConverter应用于字典中列表中的值? [英] How to apply a custom JsonConverter to the values inside a list inside a dictionary?

查看:97
本文介绍了如何将自定义JsonConverter应用于字典中列表中的值?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个 CustomConverter:JsonConverter< int> 用于整数,我需要向添加 [JsonConverter(typeof(CustomConverter))] 属性字典< string,List< int>> 属性.将自定义转换器应用于 int List Dictionary 可以正常工作:

I have a CustomConverter : JsonConverter<int> for integers, and I need to add a [JsonConverter(typeof(CustomConverter))] attribute to a Dictionary<string, List<int>> property. Applying the custom converter to an int, List or Dictionary works fine:

public class Example 
{
    [JsonConverter(typeof(CustomConverter))]
    public int ExampleInt { get; set; }
    [JsonProperty(ItemConverterType = typeof(CustomConverter))]
    public List<int> ExampleList { get; set; }
    
    // How do I specify the Converter attribute for the int in the following line?
    public Dictionary<string, List<int>> ExampleDictionary { get; set; }
}

但是我不知道如何指定CustomConverter应该用于 Dictionary List 内的 int 值.我该怎么办?

However I can't figure out how to specify that the CustomConverter should be used for the int values inside the List inside the Dictionary. How can I do this?

推荐答案

Dictionary< string,List< int>> 是一个嵌套的集合集合,您正在寻找类似 ItemOfItemsConverterType ,对应于 ItemConverterType ,以指定集合项的转换器.不幸的是,没有实现这样的属性.相反,将有必要为嵌套的 List< int> 集合创建一个转换器,以调用所需的最里面的项目转换器.

Dictionary<string, List<int>> is a nested collection of collections, and you are looking for something like ItemOfItemsConverterType, corresponding to ItemConverterType, to specify a converter for the items of the items of the collection. Unfortunately, no such attribute is implemented. Instead, it will be necessary to create a converter for the nested List<int> collection that calls the required innermost item converter.

这可以通过实现以下 JsonConverter 装饰器来完成.对于 List<> :

This can be done by implementing the following JsonConverter decorator for List<>:

public class ListItemConverterDecorator : JsonConverter
{
    readonly JsonConverter itemConverter;
    
    public ListItemConverterDecorator(Type type) => 
        itemConverter = (JsonConverter)Activator.CreateInstance(type ?? throw new ArgumentNullException());

    public override bool CanConvert(Type objectType) =>
        !objectType.IsPrimitive && objectType != typeof(string) && objectType.BaseTypesAndSelf().Any(t => t.IsGenericType && t.GetGenericTypeDefinition() == typeof(List<>));
    
    public override bool CanRead => itemConverter.CanRead;
    public override bool CanWrite => itemConverter.CanWrite;
    
    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        var itemType = objectType.BaseTypesAndSelf().Where(t => t.IsGenericType && t.GetGenericTypeDefinition() == typeof(List<>)).Select(t => t.GetGenericArguments()[0]).First();
        if (reader.MoveToContentAndAssert().TokenType == JsonToken.Null)
            return null;
        if (reader.TokenType != JsonToken.StartArray)
            throw new JsonSerializationException(string.Format("Unexpected token {0}, expected {1}", reader.TokenType, JsonToken.StartArray));
        var list = existingValue as IList ?? (IList)serializer.ContractResolver.ResolveContract(objectType).DefaultCreator();
        while (reader.ReadToContentAndAssert().TokenType != JsonToken.EndArray)
            list.Add(itemConverter.ReadJson(reader, itemType, null, serializer));
        return list;
    }
    
    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        writer.WriteStartArray();
        foreach (var item in (IList)value)
            if (item == null)
                writer.WriteNull();
            else
                itemConverter.WriteJson(writer, item, serializer);
        writer.WriteEndArray();
    }
}

public static partial class JsonExtensions
{
    public static JsonReader ReadToContentAndAssert(this JsonReader reader) =>
        reader.ReadAndAssert().MoveToContentAndAssert();

    public static JsonReader MoveToContentAndAssert(this JsonReader reader)
    {
        if (reader == null)
            throw new ArgumentNullException();
        if (reader.TokenType == JsonToken.None)       // Skip past beginning of stream.
            reader.ReadAndAssert();
        while (reader.TokenType == JsonToken.Comment) // Skip past comments.
            reader.ReadAndAssert();
        return reader;
    }

    public static JsonReader ReadAndAssert(this JsonReader reader)
    {
        if (reader == null)
            throw new ArgumentNullException();
        if (!reader.Read())
            throw new JsonReaderException("Unexpected end of JSON stream.");
        return reader;
    }
}

public static class TypeExtensions
{
    public static IEnumerable<Type> BaseTypesAndSelf(this Type type)
    {
        while (type != null)
        {
            yield return type;
            type = type.BaseType;
        }
    }
}

然后使用Example 类,如下所示> JsonPropertyAttribute.ItemConverterParameters 指定内部项目转换器 CustomConverter :

Then annotate your Example class as follows, using JsonPropertyAttribute.ItemConverterParameters to specify the inner item converter CustomConverter:

public class Example 
{
    [JsonConverter(typeof(CustomConverter))]
    public int ExampleInt { get; set; }
    [JsonProperty(ItemConverterType = typeof(CustomConverter))]
    public List<int> ExampleList { get; set; }
    
    [JsonProperty(ItemConverterType = typeof(ListItemConverterDecorator), 
                  ItemConverterParameters = new object [] { typeof(CustomConverter) })]
    public Dictionary<string, List<int>> ExampleDictionary { get; set; }
}

现在您应该已经准备就绪.演示小提琴此处.

And now you should be all set. Demo fiddle here.

这篇关于如何将自定义JsonConverter应用于字典中列表中的值?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆