动态表格查询: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:表示前一个条件与当前是 ANDKey = 1:表示前一个条件与当前是 OR
注意:这种结构本质上是线性展开的 AST(抽象语法树),但保留了括号的嵌套。
✅ 二、.NET 类定义
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 序列化会更复杂。
✅ 三、解析逻辑表达式(推荐方案)
直接解析字符串非常复杂(需处理优先级、括号、词法分析)。强烈建议使用现成的表达式解析库,如:
- NReco.LambdaParser(轻量)
- DynamicExpresso
- 或自己写一个简易递归下降解析器(适用于固定格式)
但你的需求是生成特定结构,而非执行表达式,因此我们可以自定义一个简易解析器,假设输入格式规范:
- 所有比较都是
字段=值 - 仅支持
and/or(不区分大小写) - 括号用于分组
- 无空格敏感(但建议标准化)
✅ 四、简易解析器实现(基于递归 + 栈)
我们将表达式转为 AST(抽象语法树),再转为目标结构。
步骤:
- 词法分析:拆分为 token(字段、=、值、and/or、括号)
- 语法分析:构建 AST(考虑 and 优先级高于 or)
- 转换:将 AST 转为你的
ConditionNode列表
为简化,这里假设输入已标准化(如
a=1 AND (b=1 OR c=1) AND d=1 OR (e=1 AND f=1))
✅ 五、完整代码实现(.NET 6+)
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
