You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.27 10:08:56