如何使用C#获取JSON项数量及通过JObject遍历JSON树
C# 使用JObject遍历JSON树、统计JSON项数量实现方案
所有实现基于Newtonsoft.Json(Json.NET),使用前需要先引入Newtonsoft.Json.Linq命名空间。
遍历JSON树结构
JSON本身是递归嵌套的结构,JToken是JObject(JSON对象)、JArray(JSON数组)、JValue(基础值类型:字符串/数字/布尔/null)的公共基类,遍历核心逻辑是判断当前节点类型,递归/迭代处理子节点即可。
通用递归遍历实现
适合绝大多数常规场景,遍历过程中会输出每个节点的JSONPath路径、类型和值,方便定位:
/// <summary> /// 深度优先遍历JSON树 /// </summary> /// <param name="node">当前遍历节点</param> /// <param name="parentPath">当前节点的父级JSONPath路径</param> public static void TraverseJsonTree(JToken node, string parentPath = "$") { switch (node.Type) { case JTokenType.Object: // 遍历对象下的所有属性 foreach (JProperty prop in node.Children<JProperty>()) { string currentPath = $"{parentPath}.{prop.Name}"; Console.WriteLine($"[对象属性] 路径:{currentPath} | 属性名:{prop.Name} | 值类型:{prop.Value.Type}"); TraverseJsonTree(prop.Value, currentPath); } break; case JTokenType.Array: // 遍历数组下的所有元素,记录索引 int index = 0; foreach (JToken item in node.Children()) { string currentPath = $"{parentPath}[{index}]"; Console.WriteLine($"[数组元素] 路径:{currentPath} | 索引:{index} | 值类型:{item.Type}"); TraverseJsonTree(item, currentPath); index++; } break; default: // 叶子节点,无下级子节点 Console.WriteLine($"[叶子节点] 路径:{parentPath} | 值:{node}"); break; } }
调用方式:
// 解析JSON字符串,根为对象时可用JObject,不确定根类型时直接用JToken.Parse兼容所有场景 JToken root = JToken.Parse(待处理的JSON字符串内容); TraverseJsonTree(root);
非递归遍历实现
如果要处理嵌套层级极深(超过1000层)的JSON,避免递归栈溢出,可以用栈实现迭代遍历,逻辑和递归一致:
public static void TraverseJsonTreeWithoutRecursion(JToken root) { Stack<(JToken Node, string Path)> traverseStack = new Stack<(JToken, string)>(); traverseStack.Push((root, "$")); while (traverseStack.Count > 0) { var (currentNode, currentPath) = traverseStack.Pop(); if (currentNode is JObject obj) { // Reverse是为了保证遍历顺序和递归逻辑一致,不需要顺序一致可以去掉 foreach (JProperty prop in obj.Properties().Reverse()) { traverseStack.Push((prop.Value, $"{currentPath}.{prop.Name}")); } } else if (currentNode is JArray arr) { for (int i = arr.Count - 1; i >= 0; i--) { traverseStack.Push((arr[i], $"{currentPath}[{i}]")); } } else { Console.WriteLine($"[叶子节点] 路径:{currentPath} | 值:{currentNode}"); } } }
统计JSON项数量
统计前先明确统计维度,常见的统计项包括叶子节点总数、对象属性总数、数组元素总数、对象/数组节点总数,直接基于遍历逻辑累加计数即可:
/// <summary> /// JSON结构统计结果 /// </summary> public class JsonCountResult { /// <summary>叶子值节点总数</summary> public int LeafNodeCount { get; set; } /// <summary>对象节点总数</summary> public int ObjectCount { get; set; } /// <summary>对象属性总数</summary> public int PropertyCount { get; set; } /// <summary>数组节点总数</summary> public int ArrayCount { get; set; } /// <summary>数组元素总数</summary> public int ArrayItemCount { get; set; } } public static JsonCountResult CountJsonItems(JToken root) { JsonCountResult result = new JsonCountResult(); Stack<JToken> stack = new Stack<JToken>(); stack.Push(root); while (stack.Count > 0) { JToken current = stack.Pop(); switch (current.Type) { case JTokenType.Object: result.ObjectCount++; foreach (JProperty prop in current.Children<JProperty>()) { result.PropertyCount++; stack.Push(prop.Value); } break; case JTokenType.Array: result.ArrayCount++; foreach (JToken item in current.Children()) { result.ArrayItemCount++; stack.Push(item); } break; default: result.LeafNodeCount++; break; } } return result; }
注意事项
- 如果JSON根节点是数组,不要用
JObject.Parse解析,直接用JToken.Parse可以兼容根为对象、数组、单值的所有场景 - 如果只是查找特定名称/特定值的节点,遍历到目标节点后可以直接终止遍历,不需要走完整个树,性能更好
- 对于大小超过100MB的超大JSON文件,不建议直接加载为JObject全量遍历,应该用流式读取的方式处理,避免内存占用过高
内容的提问来源于stack exchange,提问作者ZEM
相关产品推荐
相关产品推荐

