C#解析类GraphQL格式文本遇循环问题,求对象属性识别方案
嘿,我完全懂你现在的困扰——这种类似GraphQL查询的嵌套文本结构,用普通循环处理确实很容易陷入分组混乱或者死循环,毕竟它的层级是递归嵌套的,换递归思路来处理会顺畅很多!
解决思路:用递归处理嵌套对象
这种带{}的嵌套结构,每一个{}都对应一个对象,里面要么是普通属性(后面没有{}),要么是子对象(后面跟着{})。递归可以自然匹配每一层的{},避免手动维护层级栈的麻烦。
第一步:定义数据模型
先创建对应的类来存储解析后的对象、属性和子对象关系:
// 表示对象的普通属性 public class Field { public string Name { get; set; } } // 表示核心对象,包含自身属性和子对象 public class ObjectType { public string Name { get; set; } public List<Field> Fields { get; set; } = new List<Field>(); public List<ObjectType> ChildObjects { get; set; } = new List<ObjectType>(); }
第二步:核心递归解析方法
下面是完整的C#解析代码,核心是用递归处理每一层的对象,同时通过索引指针跟踪当前遍历位置:
using System; using System.Collections.Generic; using System.Text.RegularExpressions; public class QueryParser { public static ObjectType ParseQuery(string query) { // 先清理输入文本:去掉多余空格,保留关键的{}和名称 var cleanedQuery = Regex.Replace(query, @"\s+", " ").Trim(); var currentIndex = 0; // 解析根对象 return ParseNestedObject(cleanedQuery, ref currentIndex); } // 递归解析单个对象 private static ObjectType ParseNestedObject(string input, ref int index) { var currentObj = new ObjectType(); // 跳过前面的字符,找到对象开头的{ while (index < input.Length && input[index] != '{') { index++; } index++; // 跳过{ // 提取对象名称(从当前位置到下一个空格或{) var nameStartPos = index; while (index < input.Length && input[index] != ' ' && input[index] != '{') { index++; } currentObj.Name = input.Substring(nameStartPos, index - nameStartPos).Trim(); // 处理当前对象的属性和子对象 while (index < input.Length && input[index] != '}') { // 跳过空格,定位到下一个字段/子对象的开头 while (index < input.Length && char.IsWhiteSpace(input[index])) { index++; } if (index >= input.Length || input[index] == '}') break; // 提取当前字段/子对象的名称 var fieldNameStart = index; while (index < input.Length && input[index] != ' ' && input[index] != '{') { index++; } var itemName = input.Substring(fieldNameStart, index - fieldNameStart).Trim(); // 判断是普通属性还是子对象:检查下一个非空格字符是否为{ var tempIdx = index; while (tempIdx < input.Length && char.IsWhiteSpace(input[tempIdx])) { tempIdx++; } if (tempIdx < input.Length && input[tempIdx] == '{') { // 是子对象,递归解析 var childObj = ParseNestedObject(input, ref index); currentObj.ChildObjects.Add(childObj); } else { // 是普通属性,添加到字段列表 currentObj.Fields.Add(new Field { Name = itemName }); } } // 跳过对象结尾的} if (index < input.Length && input[index] == '}') { index++; } return currentObj; } }
使用示例
你可以用你的测试文本跑一下这个解析器:
var testQuery = "{ books{ isbn name author{ id name birthdate } publisher{ id name address } } }"; var parsedRoot = QueryParser.ParseQuery(testQuery); // 打印解析结果,验证结构 Console.WriteLine($"根对象:{parsedRoot.Name}"); Console.WriteLine("它的属性:"); foreach (var field in parsedRoot.Fields) { Console.WriteLine($"- {field.Name}"); } Console.WriteLine("它的子对象:"); foreach (var child in parsedRoot.ChildObjects) { Console.WriteLine($"- {child.Name}"); Console.WriteLine($" 子对象属性:"); foreach (var f in child.Fields) { Console.WriteLine($" - {f.Name}"); } }
运行后就能清晰看到books的属性(isbn、name),以及它的子对象author、publisher各自的属性,完全符合你的需求。
关键注意点
- 清理输入:先把多余的空格去掉,避免解析时被空格干扰;
- 索引跟踪:用引用传递的索引指针,确保递归调用时不会重复遍历文本;
- 边界判断:每一步都要检查索引是否越界,避免抛出异常;
- 递归终止条件:当遇到
}时,当前层级的对象解析完成,返回给上一层。
内容的提问来源于stack exchange,提问作者Aaron Gibson
相关产品推荐
相关产品推荐

