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

C#中的JSON序列化——嵌套对象展平:能否将UserStatus的所有属性直接输出到JSON根层级

实现嵌套对象属性的JSON展平(Newtonsoft.Json)

当然可以实现!用你代码里用到的Newtonsoft.Json(JsonConvert)的自定义转换器或者契约解析器,就能把UserStatus的属性直接展平到JSON根层级,下面给你两种实用方案:

方案一:自定义JsonConverter(针对单个类)

这种方法适合只需要给User类做展平的场景,代码直观,容易控制细节:

1. 编写自定义转换器

public class UserFlattenConverter : JsonConverter<User>
{
    // 处理序列化逻辑
    public override void WriteJson(JsonWriter writer, User value, JsonSerializer serializer)
    {
        // 先将User对象转为JObject,方便操作属性
        var rootObj = JObject.FromObject(value);
        
        // 取出嵌套的Status对象
        var statusObj = rootObj["Status"] as JObject;
        if (statusObj != null)
        {
            // 把Status的所有属性移到根对象
            foreach (var prop in statusObj.Properties())
            {
                rootObj.Add(prop.Name, prop.Value);
            }
            // 删除原来的Status嵌套字段
            rootObj.Remove("Status");
        }
        
        // 序列化最终的JObject
        rootObj.WriteTo(writer);
    }

    // 处理反序列化逻辑(如果需要把JSON转回来的话)
    public override User ReadJson(JsonReader reader, Type objectType, User existingValue, bool hasExistingValue, JsonSerializer serializer)
    {
        var rootObj = JObject.Load(reader);
        var user = new User
        {
            UserName = rootObj["UserName"]?.ToString()
        };
        
        // 把根层级的Id和Status属性还原到UserStatus对象
        user.Status = new UserStatus
        {
            Id = rootObj["Id"]?.ToObject<int>() ?? 0,
            Status = rootObj["Status"]?.ToString()
        };
        
        return user;
    }

    public override bool CanRead => true; // 允许反序列化
    public override bool CanWrite => true; // 允许序列化
}

2. 使用转换器序列化

// 初始化测试对象
var user = new User
{
    UserName = "test",
    Status = new UserStatus { Id = 1, Status = "status" }
};

// 配置序列化设置,添加自定义转换器
var jsonSettings = new JsonSerializerSettings
{
    Converters = new List<JsonConverter> { new UserFlattenConverter() }
};

// 序列化得到展平后的JSON
string json = JsonConvert.SerializeObject(user, jsonSettings);
// 输出结果:{"UserName":"test","Id":1,"Status":"status"}

方案二:自定义ContractResolver(通用场景)

如果你的项目里有多个类需要展平嵌套属性,用契约解析器更通用,不需要为每个类单独写转换器:

1. 编写自定义契约解析器

public class FlattenNestedContractResolver : DefaultContractResolver
{
    protected override IList<JsonProperty> CreateProperties(Type type, MemberSerialization memberSerialization)
    {
        var originalProps = base.CreateProperties(type, memberSerialization);
        var flattenedProps = new List<JsonProperty>();

        foreach (var prop in originalProps)
        {
            // 这里指定需要展平的嵌套类型(比如UserStatus)
            if (prop.PropertyType == typeof(UserStatus))
            {
                // 获取嵌套对象的所有属性
                var nestedProps = base.CreateProperties(prop.PropertyType, memberSerialization);
                foreach (var nestedProp in nestedProps)
                {
                    // 自定义值提供器,从嵌套对象中读取/写入属性值
                    nestedProp.ValueProvider = new FlattenedValueProvider(prop.ValueProvider, nestedProp.ValueProvider);
                    flattenedProps.Add(nestedProp);
                }
            }
            else
            {
                // 非嵌套属性直接保留
                flattenedProps.Add(prop);
            }
        }

        return flattenedProps;
    }

    // 自定义值提供器,处理嵌套属性的取值和赋值
    private class FlattenedValueProvider : IValueProvider
    {
        private readonly IValueProvider _parentValueProvider;
        private readonly IValueProvider _nestedValueProvider;

        public FlattenedValueProvider(IValueProvider parentProvider, IValueProvider nestedProvider)
        {
            _parentValueProvider = parentProvider;
            _nestedValueProvider = nestedProvider;
        }

        public object GetValue(object target)
        {
            // 先获取父对象的嵌套属性实例,再读取嵌套属性的值
            var nestedObj = _parentValueProvider.GetValue(target);
            return nestedObj != null ? _nestedValueProvider.GetValue(nestedObj) : null;
        }

        public void SetValue(object target, object value)
        {
            // 先获取父对象的嵌套属性实例,再给嵌套属性赋值
            var nestedObj = _parentValueProvider.GetValue(target);
            if (nestedObj != null)
            {
                _nestedValueProvider.SetValue(nestedObj, value);
            }
        }
    }
}

2. 使用契约解析器序列化

var jsonSettings = new JsonSerializerSettings
{
    ContractResolver = new FlattenNestedContractResolver()
};

string json = JsonConvert.SerializeObject(user, jsonSettings);
// 同样得到展平后的JSON结构

注意事项

  • 如果User和UserStatus存在同名属性,序列化时会出现属性覆盖,需要额外处理(比如给嵌套属性加前缀)。
  • 如果不需要反序列化,可以简化转换器里的ReadJson方法(比如直接抛出NotImplementedException)。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.30 03:12:40