如何将自定义JsonConverter应用于字典中列表中的值? [英] How to apply a custom JsonConverter to the values inside a list inside a dictionary?
问题描述
我有一个 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屋!