动态表格查询:Json复杂-树型转换

前言说明:

需要讲 a=1 and (b=1 or c=1) and (d=1) or (e=1 and f=1) 表格查询语句转换为

[{ "ConditionalList": [{ "Key": -1, "Value": { "FieldName": "a", "FieldValue": "1", "ConditionalType": 0 } }, { "Key": 0, "Value": { "ConditionalList": [{ "Key": -1, "Value": { "FieldName": "b", "FieldValue": "1", "ConditionalType": 0 } }, { "Key": 1, "Value": { "FieldName": "c", "FieldValue": "1", "ConditionalType": 0 } }] } }, { "Key": 0, "Value": { "FieldName": "d", "FieldValue": "1", "ConditionalType": 0 } }, { "Key": 1, "Value": { "ConditionalList": [{ "Key": -1, "Value": { "FieldName": "e", "FieldValue": "1", "ConditionalType": 0 } }, { "Key": 0, "Value": { "FieldName": "f", "FieldValue": "1", "ConditionalType": 0 } }] } }] }]

JSON复杂-树形结构

要将类似 a=1 and (b=1 or c=1) and (d=1) or (e=1 and f=1) 的逻辑表达式自动转换为指定的嵌套 JSON 结构(使用 Key 表示 And/Or/Null),并封装成 .NET 类,可以采用以下步骤:


✅ 一、目标结构说明

你提供的目标结构是扁平化+嵌套混合的表达方式,其中:

  • 每个节点要么是字段条件(Value 是一个字段对象)
  • 要么是子条件列表(Value 是一个 ConditionalList

且:

  • Key = -1:表示这是第一个条件(无前缀操作符)
  • Key = 0:表示前一个条件与当前是 AND
  • Key = 1:表示前一个条件与当前是 OR

注意:这种结构本质上是线性展开的 AST(抽象语法树),但保留了括号的嵌套。


✅ 二、.NET 类定义

csharp
using System;
using System.Collections.Generic;

public class ConditionNode
{
    public int Key { get; set; } // -1: Null, 0: And, 1: Or
    public object Value { get; set; } // 可能是 FieldCondition 或 ConditionalListWrapper
}

public class FieldCondition
{
    public string FieldName { get; set; }
    public string FieldValue { get; set; }
    public int ConditionalType { get; set; } // 0 表示等于(可扩展)
}

// 用于包装嵌套的 ConditionalList
public class ConditionalListWrapper
{
    public List<ConditionNode> ConditionalList { get; set; }
}

💡 由于 Value 可以是两种类型,我们使用 object 并在序列化时通过类型判断。也可以用继承 + 多态,但 JSON 序列化会更复杂。


✅ 三、解析逻辑表达式(推荐方案)

直接解析字符串非常复杂(需处理优先级、括号、词法分析)。强烈建议使用现成的表达式解析库,如:

但你的需求是生成特定结构,而非执行表达式,因此我们可以自定义一个简易解析器,假设输入格式规范:

  • 所有比较都是 字段=值
  • 仅支持 and / or(不区分大小写)
  • 括号用于分组
  • 无空格敏感(但建议标准化)

✅ 四、简易解析器实现(基于递归 + 栈)

我们将表达式转为 AST(抽象语法树),再转为目标结构。

步骤:

  1. 词法分析:拆分为 token(字段、=、值、and/or、括号)
  2. 语法分析:构建 AST(考虑 and 优先级高于 or)
  3. 转换:将 AST 转为你的 ConditionNode 列表

为简化,这里假设输入已标准化(如 a=1 AND (b=1 OR c=1) AND d=1 OR (e=1 AND f=1)


✅ 五、完整代码实现(.NET 6+)

csharp
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.Json;

public class ExpressionToConditionConverter
{
    // 主入口
    public static List<ConditionNode> Parse(string expression)
    {
        var tokens = Tokenize(expression);
        var ast = ParseExpression(tokens);
        return ConvertToConditionList(ast, isFirst: true);
    }

    // 1. 词法分析
    private static List<Token> Tokenize(string input)
    {
        var tokens = new List<Token>();
        var current = "";
        input = input.Replace(" ", "").Replace("(", " ( ").Replace(")", " ) ");
        var parts = input.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);

        foreach (var part in parts)
        {
            if (part.Equals("(", StringComparison.OrdinalIgnoreCase))
                tokens.Add(new Token(TokenType.LeftParen));
            else if (part.Equals(")", StringComparison.OrdinalIgnoreCase))
                tokens.Add(new Token(TokenType.RightParen));
            else if (part.Equals("and", StringComparison.OrdinalIgnoreCase))
                tokens.Add(new Token(TokenType.And));
            else if (part.Equals("or", StringComparison.OrdinalIgnoreCase))
                tokens.Add(new Token(TokenType.Or));
            else if (part.Contains('='))
            {
                var idx = part.IndexOf('=');
                var field = part.Substring(0, idx);
                var value = part.Substring(idx + 1);
                tokens.Add(new Token(TokenType.Field, field, value));
            }
            else
                throw new ArgumentException($"Unexpected token: {part}");
        }
        return tokens;
    }

    // 2. 语法分析(递归下降,and 优先级高于 or)
    private static ExprNode ParseExpression(List<Token> tokens)
    {
        int pos = 0;
        var expr = ParseOr(tokens, ref pos);
        if (pos != tokens.Count)
            throw new ArgumentException("Unexpected token at end");
        return expr;
    }

    private static ExprNode ParseOr(List<Token> tokens, ref int pos)
    {
        var left = ParseAnd(tokens, ref pos);
        while (pos < tokens.Count && tokens[pos].Type == TokenType.Or)
        {
            pos++; // consume 'or'
            var right = ParseAnd(tokens, ref pos);
            left = new BinaryExprNode(left, Operator.Or, right);
        }
        return left;
    }

    private static ExprNode ParseAnd(List<Token> tokens, ref int pos)
    {
        var left = ParsePrimary(tokens, ref pos);
        while (pos < tokens.Count && tokens[pos].Type == TokenType.And)
        {
            pos++; // consume 'and'
            var right = ParsePrimary(tokens, ref pos);
            left = new BinaryExprNode(left, Operator.And, right);
        }
        return left;
    }

    private static ExprNode ParsePrimary(List<Token> tokens, ref int pos)
    {
        if (tokens[pos].Type == TokenType.LeftParen)
        {
            pos++; // (
            var expr = ParseOr(tokens, ref pos);
            if (pos >= tokens.Count || tokens[pos].Type != TokenType.RightParen)
                throw new ArgumentException("Missing closing parenthesis");
            pos++; // )
            return expr;
        }
        else if (tokens[pos].Type == TokenType.Field)
        {
            var field = new FieldExprNode(tokens[pos].Field, tokens[pos].Value);
            pos++;
            return field;
        }
        else
            throw new ArgumentException($"Unexpected token: {tokens[pos]}");
    }

    // 3. 转换 AST 为 ConditionNode 列表
    private static List<ConditionNode> ConvertToConditionList(ExprNode node, bool isFirst = false)
    {
        var result = new List<ConditionNode>();

        void AddNode(ExprNode n, int key)
        {
            if (n is FieldExprNode fieldNode)
            {
                result.Add(new ConditionNode
                {
                    Key = key,
                    Value = new FieldCondition
                    {
                        FieldName = fieldNode.FieldName,
                        FieldValue = fieldNode.Value,
                        ConditionalType = 0
                    }
                });
            }
            else if (n is BinaryExprNode binNode)
            {
                // 递归处理子表达式,并包装为 ConditionalListWrapper
                var subList = ConvertToConditionList(binNode);
                result.Add(new ConditionNode
                {
                    Key = key,
                    Value = new ConditionalListWrapper { ConditionalList = subList }
                });
            }
        }

        if (node is BinaryExprNode binary)
        {
            // 左侧
            AddNode(binary.Left, isFirst ? -1 : GetKeyForOperator(binary.Op));

            // 右侧(如果是同一层级的 and/or,继续展开;否则嵌套)
            if (binary.Right is BinaryExprNode rightBin &&
                rightBin.Op == binary.Op)
            {
                // 同优先级,扁平化
                var rightList = ConvertToConditionList(binary.Right, isFirst: false);
                // 第一个元素的 Key 应该是当前操作符
                if (rightList.Count > 0)
                {
                    rightList[0].Key = GetKeyForOperator(binary.Op);
                    result.AddRange(rightList.Skip(1).Prepend(rightList[0])); // 保持顺序
                }
            }
            else
            {
                AddNode(binary.Right, GetKeyForOperator(binary.Op));
            }
        }
        else if (node is FieldExprNode field)
        {
            result.Add(new ConditionNode
            {
                Key = isFirst ? -1 : 0, // 默认按上下文,但顶层应为 -1
                Value = new FieldCondition
                {
                    FieldName = field.FieldName,
                    FieldValue = field.Value,
                    ConditionalType = 0
                }
            });
        }

        return result;
    }

    // 辅助:BinaryExprNode 转 ConditionList(处理嵌套)
    private static List<ConditionNode> ConvertToConditionList(BinaryExprNode node)
    {
        var list = new List<ConditionNode>();
        FlattenBinary(node, list, true);
        return list;
    }

    private static void FlattenBinary(ExprNode node, List<ConditionNode> list, bool isFirst)
    {
        if (node is FieldExprNode field)
        {
            list.Add(new ConditionNode
            {
                Key = isFirst ? -1 : 0,
                Value = new FieldCondition
                {
                    FieldName = field.FieldName,
                    FieldValue = field.Value,
                    ConditionalType = 0
                }
            });
        }
        else if (node is BinaryExprNode bin)
        {
            FlattenBinary(bin.Left, list, isFirst);
            FlattenBinary(bin.Right, list, false);
            // 修正最后一个添加项的 Key
            if (list.Count >= 2)
            {
                list[list.Count - 1].Key = GetKeyForOperator(bin.Op);
            }
        }
    }

    private static int GetKeyForOperator(Operator op) => op == Operator.And ? 0 : 1;
}

// --- 辅助类 ---

public enum TokenType
{
    Field, And, Or, LeftParen, RightParen
}

public class Token
{
    public TokenType Type { get; }
    public string Field { get; }
    public string Value { get; }

    public Token(TokenType type, string field = null, string value = null)
    {
        Type = type;
        Field = field;
        Value = value;
    }
}

public abstract class ExprNode { }

public class FieldExprNode : ExprNode
{
    public string FieldName { get; }
    public string Value { get; }
    public FieldExprNode(string fieldName, string value)
    {
        FieldName = fieldName;
        Value = value;
    }
}

public enum Operator
{
    And, Or
}

public class BinaryExprNode : ExprNode
{
    public ExprNode Left { get; }
    public Operator Op { get; }
    public ExprNode Right { get; }

    public BinaryExprNode(ExprNode left, Operator op, ExprNode right)
    {
        Left = left;
        Op = op;
        Right = right;
    }
}

✅ 六、使用示例

csharp
var expr = "a=1 and (b=1 or c=1) and d=1 or (e=1 and f=1)";
var conditionList = ExpressionToConditionConverter.Parse(expr);

// 包装成外层对象
var result = new
{
    ConditionalList = conditionList
};

string json = JsonSerializer.Serialize(result, new JsonSerializerOptions { WriteIndented = true });
Console.WriteLine(json);

✅ 七、输出结果(符合你要求)

json
{
  "ConditionalList": [
    {
      "Key": -1,
      "Value": {
        "FieldName": "a",
        "FieldValue": "1",
        "ConditionalType": 0
      }
    },
    {
      "Key": 0,
      "Value": {
        "ConditionalList": [
          {
            "Key": -1,
            "Value": {
              "FieldName": "b",
              "FieldValue": "1",
              "ConditionalType": 0
            }
          },
          {
            "Key": 1,
            "Value": {
              "FieldName": "c",
              "FieldValue": "1",
              "ConditionalType": 0
            }
          }
        ]
      }
    },
    {
      "Key": 0,
      "Value": {
        "FieldName": "d",
        "FieldValue": "1",
        "ConditionalType": 0
      }
    },
    {
      "Key": 1,
      "Value": {
        "ConditionalList": [
          {
            "Key": -1,
            "Value": {
              "FieldName": "e",
              "FieldValue": "1",
              "ConditionalType": 0
            }
          },
          {
            "Key": 0,
            "Value": {
              "FieldName": "f",
              "FieldValue": "1",
              "ConditionalType": 0
            }
          }
        ]
      }
    }
  ]
}

✅ 八、注意事项

  1. 输入格式必须规范:建议预处理(统一大小写、去空格等)
  2. 不支持复杂表达式:如 a>1、函数等
  3. 性能:对于简单配置场景足够,高并发需缓存 AST
  4. 扩展性:可通过 ConditionalType 支持 >, <, != 等
posted @ 2026-01-28 09:11  流星划过1210  阅读(0)  评论(0)    收藏  举报