You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在.NET中反序列化可能为标量或数组的JSON属性?

解决JSON标量/数组属性反序列化为C#集合的通用方案

针对JSON属性可能是字符串或字符串数组,需要统一反序列化为IReadOnlyCollection<string>或List<string>的场景,以下是几种低维护成本的解决方案:

一、Json.NET 通用自定义转换器(推荐,兼容现有代码)

编写一个通用转换器,可处理任意类型的标量转集合,无需为每个类单独编写转换器:

public class SingleOrArrayConverter<T> : JsonConverter
{
    public override bool CanConvert(Type objectType)
    {
        return typeof(IEnumerable<T>).IsAssignableFrom(objectType) || objectType == typeof(T);
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        var tokenType = reader.TokenType;
        List<T> itemList;

        if (tokenType == JsonToken.StartArray)
        {
            itemList = serializer.Deserialize<List<T>>(reader);
        }
        else
        {
            var singleItem = serializer.Deserialize<T>(reader);
            itemList = new List<T> { singleItem };
        }

        // 根据目标类型返回对应集合实例
        if (objectType.IsInterface)
        {
            switch (objectType)
            {
                case Type t when t == typeof(IReadOnlyCollection<T>):
                    return itemList.AsReadOnly();
                case Type t when t == typeof(IEnumerable<T>) || t == typeof(IList<T>):
                    return itemList;
                default:
                    throw new JsonSerializationException($"不支持转换为接口类型: {objectType}");
            }
        }
        else if (objectType == typeof(List<T>))
        {
            return itemList;
        }

        throw new JsonSerializationException($"无法转换为目标类型: {objectType}");
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        // 序列化时默认输出数组,若需单元素输出标量可自行修改逻辑
        serializer.Serialize(writer, value);
    }
}

使用方式

  1. 全局生效:注册到序列化设置,所有符合条件的属性自动处理
var settings = new JsonSerializerSettings();
settings.Converters.Add(new SingleOrArrayConverter<string>());
var myObj = JsonConvert.DeserializeObject<MyType>(jsonStr, settings);
  1. 单个属性生效:仅对指定属性应用转换器
public record MyType(
    [JsonConverter(typeof(SingleOrArrayConverter<string>))] 
    IReadOnlyCollection<string> MyProperty);

// 或传统类写法
public class MyType2
{
    [JsonConverter(typeof(SingleOrArrayConverter<string>))]
    public List<string>? MyProperty { get; set; }
}

二、System.Text.Json 通用转换器工厂(原生无依赖)

若愿意切换到.NET内置的System.Text.Json,可通过转换器工厂实现全局自动处理:

public class SingleOrArrayConverterFactory : JsonConverterFactory
{
    public override bool CanConvert(Type typeToConvert)
    {
        return typeToConvert.IsGenericType && 
               typeof(IEnumerable<>).IsAssignableFrom(typeToConvert.GetGenericTypeDefinition()) &&
               typeToConvert.GetGenericArguments().Length == 1;
    }

    public override JsonConverter CreateConverter(Type typeToConvert, JsonSerializerOptions options)
    {
        var elementType = typeToConvert.GetGenericArguments()[0];
        var converterType = typeof(SingleOrArrayConverter<>).MakeGenericType(elementType);
        return (JsonConverter)Activator.CreateInstance(converterType)!;
    }

    private class SingleOrArrayConverter<T> : JsonConverter<IEnumerable<T>>
    {
        public override IEnumerable<T> Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
        {
            if (reader.TokenType == JsonTokenType.StartArray)
            {
                return JsonSerializer.Deserialize<List<T>>(ref reader, options)!;
            }
            else
            {
                var singleItem = JsonSerializer.Deserialize<T>(ref reader, options)!;
                return new List<T> { singleItem };
            }
        }

        public override void Write(Utf8JsonWriter writer, IEnumerable<T> value, JsonSerializerOptions options)
        {
            var itemList = value.ToList();
            if (itemList.Count == 1)
            {
                JsonSerializer.Serialize(writer, itemList[0], options);
            }
            else
            {
                JsonSerializer.Serialize(writer, itemList, options);
            }
        }
    }
}

使用方式

var options = new JsonSerializerOptions();
options.Converters.Add(new SingleOrArrayConverterFactory());
var myObj = JsonSerializer.Deserialize<MyType>(jsonStr, options);

三、高性能选项:Utf8Json

若追求极致性能,可使用第三方库Utf8Json,通过通用格式化器处理:

public class SingleOrArrayFormatter<T> : IJsonFormatter<IEnumerable<T>>
{
    public void Serialize(ref JsonWriter writer, IEnumerable<T> value, IJsonFormatterResolver formatterResolver)
    {
        var itemList = value.ToList();
        if (itemList.Count == 1)
        {
            formatterResolver.GetFormatter<T>().Serialize(ref writer, itemList[0], formatterResolver);
        }
        else
        {
            writer.WriteBeginArray();
            foreach (var item in itemList)
            {
                formatterResolver.GetFormatter<T>().Serialize(ref writer, item, formatterResolver);
            }
            writer.WriteEndArray();
        }
    }

    public IEnumerable<T> Deserialize(ref JsonReader reader, IJsonFormatterResolver formatterResolver)
    {
        if (reader.GetCurrentJsonToken() == JsonToken.BeginArray)
        {
            reader.ReadIsBeginArray();
            var itemList = new List<T>();
            while (reader.GetCurrentJsonToken() != JsonToken.EndArray)
            {
                var item = formatterResolver.GetFormatter<T>().Deserialize(ref reader, formatterResolver);
                itemList.Add(item);
                reader.Next();
            }
            reader.ReadIsEndArray();
            return itemList;
        }
        else
        {
            var singleItem = formatterResolver.GetFormatter<T>().Deserialize(ref reader, formatterResolver);
            return new List<T> { singleItem };
        }
    }
}

使用方式

var resolver = new CompositeResolver(
    new[] { new SingleOrArrayFormatter<string>() },
    StandardResolver.Default);
var myObj = JsonSerializer.Deserialize<MyType>(jsonStr, resolver);

内容的提问来源于stack exchange,提问作者solublefish

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.12 06:07:22