C# 实现动态扁平化嵌套JSON以适配DataGrid数据绑定的方案
嵌套JSON动态扁平化实现方案
需求说明
我们需要将任意层级嵌套的JSON结构自动展平为单层对象集合,适配DataGrid数据绑定场景,以下为样例参考:
- 输入层级结构:
Students->Subjects->Marks,每层均可包含多个子节点 - 输出规则:最内层的子节点会向上合并所有父级的属性,生成单层JSON对象
输入样例
[ { "student":"Bob", "subjects":[ { "subjectname":"English", "marks":[ { "type":"essay", "grade":"A" }, { "type":"vocabulary", "grade":"B" }, { "type":"spoken", "grade":"C" } ] }, { "subjectname":"French", "marks":[ { "type":"essay", "grade":"B" }, { "type":"vocabulary", "grade":"A" }, { "type":"spoken", "grade":"B" } ] } ] }, { "student":"Mark", "subjects":[ { "subjectname":"Dutch", "marks":[ { "type":"essay", "grade":"C" }, { "type":"vocabulary", "grade":"B" }, { "type":"spoken", "grade":"A" } ] }, { "subjectname":"Mandrian", "marks":[ { "type":"essay", "grade":"C" }, { "type":"vocabulary", "grade":"C" }, { "type":"spoken", "grade":"C" } ] } ] } ]
预期输出
[ { "student":"Bob", "subjectname":"English", "type":"essay", "grade":"A" }, { "student":"Bob", "subjectname":"English", "type":"vocabulary", "grade":"B" }, { "student":"Bob", "subjectname":"English", "type":"spoken", "grade":"C" }, { "student":"Bob", "subjectname":"French", "type":"essay", "grade":"B" }, { "student":"Bob", "subjectname":"French", "type":"vocabulary", "grade":"A" }, { "student":"Bob", "subjectname":"French", "type":"spoken", "grade":"B" }, ... ]
实现思路
原有代码的问题是没有处理数组生成多条结果的逻辑,会出现属性覆盖的问题,调整逻辑如下:
- 递归遍历JSON节点时维护当前已收集的父级属性集合
- 遇到数组节点时,遍历每一个子项,每个子项都单独复制一份当前父属性继续递归,避免不同数组项属性互相干扰
- 遇到叶子节点时直接写入当前属性集合,遍历到最内层无嵌套节点时,把完整对象加入结果集
- 不需要预设层级结构,支持任意层级的嵌套JSON自动展平
完整实现代码(C# 基于Newtonsoft.Json)
using Newtonsoft.Json.Linq; using System.Collections.Generic; using System.Linq; public static class JsonFlattener { /// <summary> /// 嵌套JSON扁平化方法 /// </summary> /// <param name="json">输入的嵌套JSON字符串</param> /// <returns>展平后的JSON数组字符串</returns> public static string Flatten(string json) { JToken input = JToken.Parse(json); List<JObject> result = new List<JObject>(); ProcessToken(input, new JObject(), result); return JArray.FromObject(result).ToString(); } private static void ProcessToken(JToken currentToken, JObject currentObj, List<JObject> result) { switch (currentToken.Type) { // 处理对象节点:遍历所有属性 case JTokenType.Object: bool hasNestedChild = false; foreach (var prop in currentToken.Children<JProperty>()) { // 属性值为对象/数组时继续递归 if (prop.Value.Type is JTokenType.Object or JTokenType.Array) { hasNestedChild = true; ProcessToken(prop.Value, currentObj, result); } // 叶子属性直接写入当前对象 else { // 若存在同名属性可自行调整为父级名+属性名的拼接逻辑避免覆盖 if (currentObj.ContainsKey(prop.Name)) currentObj[prop.Name] = prop.Value; else currentObj.Add(prop.Name, prop.Value); } } // 无嵌套子节点说明是最内层对象,加入结果集 if (!hasNestedChild) { result.Add(new JObject(currentObj)); } break; // 处理数组节点:每个子项复制当前属性集合单独递归 case JTokenType.Array: foreach (var item in currentToken.Children()) { JObject newObj = new JObject(currentObj); ProcessToken(item, newObj, result); } break; } } }
使用说明
- 直接调用
JsonFlattener.Flatten(输入json字符串)即可得到展平后的JSON数组,可直接转为DataTable或者绑定到DataGrid - 无需预设JSON结构,支持任意层级的嵌套数组自动展开,适合封装到公共类库复用
内容的提问来源于stack exchange,提问作者Harsha kumar G H
相关产品推荐
相关产品推荐

