如何在ASP.NET/C#中将嵌套JSON转换为DataTable(无需实体类)
嘿,我懂你为啥不想用实体类——多分支嵌套的JSON写实体类简直是个噩梦!不用急,我给你俩纯C#的方案,不用定义任何实体类就能把嵌套JSON转成DataTable,亲测好用:
方案一:使用Newtonsoft.Json(Json.NET)递归扁平化
这是很多项目里常用的JSON处理库,处理嵌套结构特别灵活。核心思路是把嵌套的JSON节点递归遍历,把每个节点的完整路径(比如PricedItineraries.AirItinerary.OriginDestinationOptions.OriginDestinationOption[0].FlightSegment[0].DepartureAirport.LocationCode)作为DataTable的列名,对应的值作为单元格内容,同时自动处理数组元素,每个数组项生成独立的行。
实现代码
首先确保你已经通过NuGet安装了Newtonsoft.Json包,然后用下面的代码:
using Newtonsoft.Json.Linq; using System.Collections.Generic; using System.Data; using System.Linq; public class JsonToDataTableConverter { // 递归扁平化JSON,生成每行的键值对列表 private static List<Dictionary<string, object>> FlattenJsonToList(JToken token, string parentPath = "", Dictionary<string, object> current = null) { var result = new List<Dictionary<string, object>>(); current = current ?? new Dictionary<string, object>(); switch (token.Type) { case JTokenType.Object: foreach (JProperty property in token.Children<JProperty>()) { var newPath = string.IsNullOrEmpty(parentPath) ? property.Name : $"{parentPath}.{property.Name}"; var childResult = FlattenJsonToList(property.Value, newPath, new Dictionary<string, object>(current)); result.AddRange(childResult); } break; case JTokenType.Array: int index = 0; foreach (JToken item in token.Children()) { var newPath = string.IsNullOrEmpty(parentPath) ? $"[{index}]" : $"{parentPath}[{index}]"; var childResult = FlattenJsonToList(item, newPath, new Dictionary<string, object>(current)); result.AddRange(childResult); index++; } break; default: current.Add(parentPath, token.ToObject<object>()); result.Add(current); break; } return result; } // 转换为DataTable public static DataTable ConvertNestedJsonToDataTable(string json) { var jObject = JObject.Parse(json); var flattenedList = FlattenJsonToList(jObject); if (flattenedList.Count == 0) return new DataTable(); var dataTable = new DataTable(); // 添加所有唯一列 var allColumns = flattenedList.SelectMany(dict => dict.Keys).Distinct(); foreach (var column in allColumns) { dataTable.Columns.Add(column); } // 填充行数据 foreach (var item in flattenedList) { var row = dataTable.NewRow(); foreach (var kvp in item) { row[kvp.Key] = kvp.Value ?? DBNull.Value; } dataTable.Rows.Add(row); } return dataTable; } }
使用方式
直接调用静态方法就行:
string yourJson = "你的嵌套JSON字符串"; DataTable resultTable = JsonToDataTableConverter.ConvertNestedJsonToDataTable(yourJson);
方案二:使用System.Text.Json(.NET Core/.NET 5+ 自带)
如果你的项目用的是.NET Core或者.NET 5+,可以直接用自带的System.Text.Json,不用额外安装包,思路和上面一致,只是解析方式换成了JsonElement。
实现代码
using System.Collections.Generic; using System.Data; using System.Linq; using System.Text.Json; public class JsonToDataTableConverter { // 递归扁平化JsonElement,生成每行的键值对列表 private static List<Dictionary<string, object>> FlattenJsonElementToList(JsonElement element, string parentPath = "", Dictionary<string, object> current = null) { var result = new List<Dictionary<string, object>>(); current = current ?? new Dictionary<string, object>(); switch (element.ValueKind) { case JsonValueKind.Object: foreach (var property in element.EnumerateObject()) { var newPath = string.IsNullOrEmpty(parentPath) ? property.Name : $"{parentPath}.{property.Name}"; var childResult = FlattenJsonElementToList(property.Value, newPath, new Dictionary<string, object>(current)); result.AddRange(childResult); } break; case JsonValueKind.Array: int index = 0; foreach (var item in element.EnumerateArray()) { var newPath = string.IsNullOrEmpty(parentPath) ? $"[{index}]" : $"{parentPath}[{index}]"; var childResult = FlattenJsonElementToList(item, newPath, new Dictionary<string, object>(current)); result.AddRange(childResult); index++; } break; default: object value; // 处理不同类型的JSON值 switch (element.ValueKind) { case JsonValueKind.String: value = element.GetString(); break; case JsonValueKind.Number: if (element.TryGetInt64(out long longValue)) value = longValue; else if (element.TryGetDouble(out double doubleValue)) value = doubleValue; else value = element.ToString(); break; case JsonValueKind.True: case JsonValueKind.False: value = element.GetBoolean(); break; case JsonValueKind.Null: value = DBNull.Value; break; default: value = element.ToString(); break; } current.Add(parentPath, value); result.Add(current); break; } return result; } // 转换为DataTable public static DataTable ConvertNestedJsonToDataTable(string json) { var options = new JsonDocumentOptions { AllowTrailingCommas = true }; using var doc = JsonDocument.Parse(json, options); var flattenedList = FlattenJsonElementToList(doc.RootElement); if (flattenedList.Count == 0) return new DataTable(); var dataTable = new DataTable(); // 添加所有唯一列 var allColumns = flattenedList.SelectMany(dict => dict.Keys).Distinct(); foreach (var column in allColumns) { dataTable.Columns.Add(column); } // 填充行数据 foreach (var item in flattenedList) { var row = dataTable.NewRow(); foreach (var kvp in item) { row[kvp.Key] = kvp.Value ?? DBNull.Value; } dataTable.Rows.Add(row); } return dataTable; } }
使用方式
和上面一样,直接调用:
string yourJson = "你的嵌套JSON字符串"; DataTable resultTable = JsonToDataTableConverter.ConvertNestedJsonToDataTable(yourJson);
一些需要注意的点
- 扁平化后的列名会包含完整的嵌套路径和数组索引(比如
OriginDestinationOption[0].FlightSegment[0].DepartureAirport.LocationCode),如果觉得列名太长,可以修改递归方法里的路径生成逻辑,比如去掉数组索引或者简化路径。 - DataTable的列类型默认会根据第一个出现的值来确定,如果后续行有不同类型的值,可能会抛出异常。如果想避免这种情况,可以在添加列的时候统一指定为
typeof(string),或者在代码里增加类型判断逻辑。 - 如果JSON里有大量重复的结构,生成的DataTable会包含所有唯一的字段,缺失的字段会自动填充
DBNull.Value。
内容的提问来源于stack exchange,提问作者NIDIN GOPI
相关产品推荐
相关产品推荐

