Newtonsoft.Json序列化时如何将CustomConfigObject属性内容平铺到父层级
针对Config模型的平铺序列化最优实现
你需要的效果完全可以通过自定义Newtonsoft.Json转换器实现,不需要为CustomConfigObject下的所有子属性写冗余的getter方法,可维护性远高于临时方案。
方案核心逻辑
完全接管Config类型的序列化过程:
- 手动输出Config类中定义的常规业务属性(比如id、name这类)
- 直接读取CustomConfigObject里的所有节点字段,平铺到当前JSON层级
- 自动跳过原始存储的CustomConfig字符串、CustomConfigObject嵌套键的输出,不会产生多余字段
具体实现
1. 编写专属序列化转换器
public class ConfigFlattenJsonConverter : JsonConverter { // 仅需要自定义序列化逻辑,反序列化因为是从数据库读取实体,不需要处理 public override bool CanWrite => true; public override bool CanRead => false; public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) { var config = (Config)value; writer.WriteStartObject(); // 遍历Config类本身的公开属性,跳过不需要输出的两个存储字段 var normalProperties = typeof(Config) .GetProperties() .Where(prop => prop.Name != nameof(Config.CustomConfig) && prop.Name != nameof(Config.CustomConfigObject)); foreach (var prop in normalProperties) { var propValue = prop.GetValue(config); writer.WritePropertyName(prop.Name); serializer.Serialize(writer, propValue, prop.PropertyType); } // 直接将CustomConfigObject的所有字段平铺到当前层级 if (config.CustomConfigObject != null) { foreach (var childNode in config.CustomConfigObject.Properties()) { writer.WritePropertyName(childNode.Name); childNode.Value.WriteTo(writer); } } writer.WriteEndObject(); } // 关闭自定义反序列化逻辑,不需要实现 public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) { throw new NotImplementedException(); } public override bool CanConvert(Type objectType) { // 仅对Config类型生效 return objectType == typeof(Config); } }
2. 转换器的使用方式
两种选择都可以,根据你的项目习惯选:
- 序列化时临时指定转换器,不需要修改Config类定义:
var serializeSettings = new JsonSerializerSettings { Converters = new List<JsonConverter> { new ConfigFlattenJsonConverter() } }; // 直接序列化整个Config数组即可得到平铺后的JSON string finalOutput = JsonConvert.SerializeObject(configList, Formatting.Indented, serializeSettings); - 给Config类加特性绑定转换器,全局生效:
[JsonConverter(typeof(ConfigFlattenJsonConverter))] public class Config { public int Id { get; set; } public string Name { get; set; } public string CustomConfig { get; set; } public JObject CustomConfigObject { get; set; } }
方案优势
- 不需要预先枚举CustomConfigObject下的所有可能字段,哪怕后续不同配置行的JSON结构有差异,也会被自动平铺,完全不用改动代码
- 不会因为嵌套键产生多余层级,输出结构完全匹配你要的无customConfigObject嵌套的目标JSON
- 可以灵活处理字段冲突:如果后续遇到Config本身属性和CustomConfigObject内字段重名的场景,只需要在转换器逻辑里加几行判断,指定优先保留哪一侧的值即可
- 自动过滤掉数据库存储用的两个原始字段,不会把冗余的内部存储内容暴露到最终输出的客户端配置blob里
内容的提问来源于stack exchange,提问作者Rob Jeffrey
相关产品推荐
相关产品推荐

