You need to enable JavaScript to run this app. 如何将存储属性键值对的对象数组JSON反序列化为C#自定义模型
优惠活动
大模型
产品
解决方案
定价
更多

如何将存储属性键值对的对象数组JSON反序列化为C#自定义模型

特殊结构JSON的反序列化实现

你的API返回的是扁平化键值对条目数组:每个数组项为「属性名+属性值」的二元组,和Newtonsoft.Json默认要求的「属性名直接作为JSON对象键」的结构不匹配,因此触发类型不兼容的反序列化异常。可通过以下两种成熟方案完成结构映射:

方案1:自定义Newtonsoft.Json转换器(推荐,无侵入业务代码)

该方案利用Newtonsoft的扩展点自定义反序列化逻辑,原有模型的属性定义完全不需要改动,也可以复用到其他同结构的业务模型上。

  1. 先定义匹配数组内单个元素的临时DTO,用来承接键值对条目:
    public class KeyValuePairItem
    {
        public string name { get; set; }
        public int ptr { get; set; }
    }
    
  2. 实现通用的数组转对象的自定义转换器,基于反射完成属性匹配赋值:
    using Newtonsoft.Json;
    using Newtonsoft.Json.Linq;
    using System.Reflection;
    
    // 泛型转换器:把键值对条目数组转换为指定类型的实例
    public class KeyValueArrayToObjectConverter<T> : JsonConverter where T : new()
    {
        // 仅需要支持反序列化,关闭序列化逻辑减少冗余
        public override bool CanWrite => false;
    
        // 序列化逻辑无需实现
        public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
        {
            throw new NotImplementedException();
        }
    
        // 核心反序列化逻辑
        public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
        {
            // 读取整个JSON数组为可操作的JToken结构
            var keyValueItems = JArray.Load(reader);
            // 实例化目标类型的空对象
            var targetInstance = new T();
    
            foreach (var item in keyValueItems)
            {
                // 提取数组元素里的属性名和属性值
                var propName = item["name"]?.ToString();
                var propValue = item["ptr"];
                
                if (string.IsNullOrEmpty(propName)) continue;
                
                // 匹配目标类型上的对应公共实例属性,忽略大小写匹配
                var targetProp = objectType.GetProperty(
                    propName, 
                    BindingFlags.Public | BindingFlags.Instance | BindingFlags.IgnoreCase);
                
                if (targetProp == null || !targetProp.CanWrite) continue;
                
                // 把值转换成属性对应的实际类型,完成赋值
                var convertedValue = propValue.ToObject(targetProp.PropertyType);
                targetProp.SetValue(targetInstance, convertedValue);
            }
            return targetInstance;
        }
    
        public override bool CanConvert(Type objectType)
        {
            return objectType == typeof(T);
        }
    }
    
  3. 给原有UserPtr类加上转换器标记,不改动原有属性结构:
    [JsonConverter(typeof(KeyValueArrayToObjectConverter<UserPtr>))]
    public class UserPtr
    {
        public int my_var1 { get; set; }
        public int my_var2 { get; set; }
        public int my_var3 { get; set; }
        public int my_var4 { get; set; }
    }
    

配置完成后,你原有编写的反序列化代码完全不需要调整,即可正常运行:

var userPtr = JsonConvert.DeserializeObject<UserPtr>(strJson);

方案2:手动分步映射(适合无法修改原有模型代码的场景)

如果不能给现有模型添加特性标记、不能修改原有类结构,可以采用两步处理的方式完成映射:

  1. 先把JSON数组反序列化为键值对列表
    var kvList = JsonConvert.DeserializeObject<List<KeyValuePairItem>>(strJson);
    
  2. 手动遍历列表,把值赋值给目标模型的对应属性:
    var userPtr = new UserPtr();
    foreach (var kv in kvList)
    {
        switch (kv.name)
        {
            case nameof(UserPtr.my_var1):
                userPtr.my_var1 = kv.ptr;
                break;
            case nameof(UserPtr.my_var2):
                userPtr.my_var2 = kv.ptr;
                break;
            case nameof(UserPtr.my_var3):
                userPtr.my_var3 = kv.ptr;
                break;
            case nameof(UserPtr.my_var4):
                userPtr.my_var4 = kv.ptr;
                break;
        }
    }
    

注意:这种硬编码映射的方式仅适合属性数量少、结构长期固定的场景,属性较多时维护成本会远高于反射自动映射的方案。

常见踩坑说明

  • 你提供的示例JSON中包含//形式的注释,标准JSON规范本身不支持注释,如果反序列化时遇到解析报错,可以在初始化序列化配置时开启宽松解析:
    var settings = new JsonSerializerSettings
    {
        // 忽略无法匹配的成员
        MissingMemberHandling = MissingMemberHandling.Ignore,
        // 跳过元数据属性的特殊处理
        MetadataPropertyHandling = MetadataPropertyHandling.Ignore
    };
    var userPtr = JsonConvert.DeserializeObject<UserPtr>(strJson, settings);
    
  • 反射匹配属性时,如果目标属性不是公共访问级别,需要调整BindingFlags的匹配规则,确保能正确读取到属性元数据。
  • 如果后续API返回的ptr字段类型不固定,可以把KeyValuePairItem中的ptr类型改为JToken,反射转换逻辑会自动适配不同的属性类型。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.31 23:54:10