.NET 6中如何将嵌套JSON扁平化并保留导航路径?
.NET 6 实现JSON嵌套对象扁平化(保留路径)
针对需求,这里提供两种基于Newtonsoft.Json(即JsonConvert/JObject)的高效实现方案:
方案一:递归遍历JObject实现扁平化
通过递归遍历JObject的所有节点,将嵌套路径用点号拼接后作为顶层键,值保留原数据。
步骤1:安装Newtonsoft.Json包
在项目中安装NuGet包:
Install-Package Newtonsoft.Json
步骤2:实现扁平化工具类
using Newtonsoft.Json; using Newtonsoft.Json.Linq; using System.Collections.Generic; public static class JsonFlattener { // 对外公开的扁平化方法 public static JObject Flatten(JObject inputJson) { var flattenedResult = new JObject(); TraverseToken(inputJson, string.Empty, flattenedResult); return flattenedResult; } // 递归遍历所有JToken节点 private static void TraverseToken(JToken token, string currentPath, JObject result) { switch (token.Type) { // 遇到对象类型,继续遍历子属性 case JTokenType.Object: foreach (JProperty property in token.Children<JProperty>()) { var newPath = string.IsNullOrEmpty(currentPath) ? property.Name : $"{currentPath}.{property.Name}"; TraverseToken(property.Value, newPath, result); } break; // 遇到数组可自行扩展逻辑,比如用`路径[索引]`格式 case JTokenType.Array: // 示例:遍历数组元素并拼接索引路径 // for (int i = 0; i < token.Count(); i++) // { // var newPath = string.IsNullOrEmpty(currentPath) // ? $"[{i}]" // : $"{currentPath}[{i}]"; // TraverseToken(token[i], newPath, result); // } break; // 非对象/数组类型,直接存入结果 default: if (!string.IsNullOrEmpty(currentPath)) { result[currentPath] = token; } break; } } }
步骤3:使用示例
// 原始JSON字符串 string originalJson = @"{ ""property1"":""value1"", ""property2"":""value2"", ""anotherObject"":{ ""property1"":""anothervalue1"", ""property2"":""anothervalue2"", ""anotherOfAnother"":{ ""property1"":""value1"" } } }"; // 解析为JObject并扁平化 JObject originalJObj = JObject.Parse(originalJson); JObject flattenedJObj = JsonFlattener.Flatten(originalJObj); // 输出格式化后的扁平化JSON Console.WriteLine(flattenedJObj.ToString(Formatting.Indented));
方案二:自定义JsonConverter实现序列化时扁平化
如果需要在序列化对象时直接输出扁平化JSON,可以自定义JsonConverter,在序列化阶段自动处理路径拼接。
实现自定义转换器
using Newtonsoft.Json; using Newtonsoft.Json.Linq; using System; public class FlattenedJsonConverter : JsonConverter { public override bool CanConvert(Type objectType) { return typeof(object).IsAssignableFrom(objectType); } // 反扁平化逻辑可自行扩展,这里暂不实现 public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) { throw new NotImplementedException("反扁平化逻辑需根据需求自行实现"); } public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) { // 将对象转为JObject后递归扁平化 var jObject = JObject.FromObject(value, serializer); var flattened = new JObject(); TraverseToken(jObject, string.Empty, flattened); flattened.WriteTo(writer); } // 复用之前的递归遍历方法 private void TraverseToken(JToken token, string currentPath, JObject result) { switch (token.Type) { case JTokenType.Object: foreach (JProperty prop in token.Children<JProperty>()) { var newPath = string.IsNullOrEmpty(currentPath) ? prop.Name : $"{currentPath}.{prop.Name}"; TraverseToken(prop.Value, newPath, result); } break; case JTokenType.Array: // 按需扩展数组处理逻辑 break; default: if (!string.IsNullOrEmpty(currentPath)) { result[currentPath] = token; } break; } } }
使用示例
// 原始对象 var originalObject = new { property1 = "value1", property2 = "value2", anotherObject = new { property1 = "anothervalue1", property2 = "anothervalue2", anotherOfAnother = new { property1 = "value1" } } }; // 使用自定义转换器序列化 string flattenedJson = JsonConvert.SerializeObject( originalObject, Formatting.Indented, new FlattenedJsonConverter() ); Console.WriteLine(flattenedJson);
效率说明
两种方案均基于Newtonsoft.Json的DOM模型(JObject)实现,递归遍历的方式在常规JSON结构下性能足够。如果处理超大JSON(GB级),可考虑流式处理方案,但大部分业务场景下递归DOM的方式已经满足需求。
内容的提问来源于stack exchange,提问作者Jose David
相关产品推荐
相关产品推荐

