【问题标题】:How to apply ItemConverterType when deserializing a jagged array?反序列化锯齿状数组时如何应用ItemConverterType?
【发布时间】:2021-07-16 23:02:09
【问题描述】:

我有正确使用我的“InternedString”转换器的现有属性:

[JsonProperty(ItemConverterType=typeof(InternedString))]
public string[] prizeTypes;

有没有一种简单的方法可以将其应用于jagged 二维字符串数组,而无需编写我自己的string[][] 转换器?

[JsonProperty(ItemConverterType=typeof(InternedString))]
public string[][] prizeTypesByRoomType;

(它给了我一个“无法将数组转换为字符串”的异常,这很明显,但我有点希望它可以递归地应用该类型)

【问题讨论】:

    标签: c# json.net deserialization


    【解决方案1】:

    Json.NET 没有使用属性将自定义 JsonConverter 应用到诸如 string [][] 之类的交错数组的项目的功能。 ItemConverterType 仅适用于最外层数组的项。

    您可以做的是应用decorator pattern 来创建一个custom JsonConverter,该custom JsonConverter 为任意深度的锯齿状数组的最内部项目包装了一些内部项目转换器。

    首先,定义如下转换器:

    public class JaggedArrayItemConverterDecorator : JsonConverter
    {
        readonly JsonConverter itemConverter;
    
        public JaggedArrayItemConverterDecorator(Type type) => 
            itemConverter = (JsonConverter)Activator.CreateInstance(type ?? throw new ArgumentNullException());
    
        public override bool CanConvert(Type objectType) => objectType.IsJaggedRankOneArray(out var itemType) && itemConverter.CanConvert(itemType);
    
        public override bool CanRead => itemConverter.CanRead;
        public override bool CanWrite => itemConverter.CanWrite;
    
        public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
        {
            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 itemType = objectType.GetElementType();
            IList list = (IList)serializer.ContractResolver.ResolveContract(typeof(List<>).MakeGenericType(itemType)).DefaultCreator();
            while (reader.ReadToContentAndAssert().TokenType != JsonToken.EndArray)
            {
                if (itemType.IsArray)
                    list.Add(ReadJson(reader, itemType, null, serializer));
                else
                    list.Add(itemConverter.ReadJson(reader, itemType, null, serializer));
            }
            var array = Array.CreateInstance(itemType, list.Count);
            list.CopyTo(array, 0);
            return array;
        }
    
        public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
        {
            var itemType = value.GetType().GetElementType();
            writer.WriteStartArray();
            foreach (var item in (IList)value)
            {
                if (item == null)
                    writer.WriteNull();
                else if (itemType.IsArray)
                    WriteJson(writer, item, serializer);
                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 bool IsJaggedRankOneArray(this Type type, out Type innermostItemType)
        {
            innermostItemType = null;
            var currentType = type ?? throw new ArgumentNullException(nameof(type));
            while (currentType.IsArray)
            {
                if (currentType.GetArrayRank() != 1)
                    return false;
                currentType = currentType.GetElementType();
            }
            if (currentType != type)
            {
                innermostItemType = currentType;
                return true;
            }
            return false;
        }
    }
    

    然后您可以将其应用于模型中任意深度的锯齿状数组,如下所示:

    public class Model
    {
        [JsonProperty(ItemConverterType=typeof(InternedString))] // For 1d arrays you can use either ItemConverterType=typeof(InternedString) or JaggedArrayItemConverterDecorator
        public string[] prizeTypes { get; set; }
    
        [JsonConverter(typeof(JaggedArrayItemConverterDecorator), typeof(InternedString))]
        public string[] prizeTypesByRoomType { get; set; }
    
        [JsonConverter(typeof(JaggedArrayItemConverterDecorator), typeof(InternedString))]
        public string[][] prizeTypesByRoomType2d { get; set; }
    
        [JsonConverter(typeof(JaggedArrayItemConverterDecorator), typeof(InternedString))]
        public string[][][] prizeTypesByRoomType3d { get; set; }
    
        [JsonConverter(typeof(JaggedArrayItemConverterDecorator), typeof(InternedString))]
        public string[][][][] prizeTypesByRoomType4d { get; set; }
        
        [JsonProperty(ItemConverterType=typeof(InternedString))] // ItemConverterType works for multidimensional arrays
        public string[,] prizeTypesMultidimensional { get; set; }
    }
    

    注意事项:

    • 转换器是为数组而不是列表实现的。如果您需要支持锯齿状列表,请参阅 How to apply a custom JsonConverter to the values inside a list inside a dictionary?

    • 转换器不适用于string [,] 等多维数组或包含多维数组的锯齿状数组。但是,ItemConverterType 实际上确实支持多维数组,因此除非您有多维数组的锯齿状数组,反之亦然,否则您应该准备就绪。

    演示小提琴here.

    【讨论】:

      猜你喜欢
      • 2021-01-20
      • 2019-04-21
      • 1970-01-01
      • 2020-04-04
      • 1970-01-01
      • 2010-12-16
      • 1970-01-01
      • 1970-01-01
      • 2011-10-09
      相关资源
      最近更新 更多