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
相关产品推荐
相关产品推荐

