
本文介绍如何在 Python 中安全、灵活地解析并执行用户通过 JSON 提供的动态条件表达式(如 (Bob.age == Jane.age) || (Bob.age本文介绍如何在 python 中安全、灵活地解析并执行用户通过 json 提供的动态条件表达式(如 `(bob.age == jane.age) || (bob.age
在实际报表、规则引擎或配置驱动型应用中,常需将业务逻辑“外置”——例如允许运营人员通过 JSON 配置动态筛选条件:
{ "filter": "(Bob.age == Jane.age) || (Bob.age < Bill.age)" }直接使用
eval()虽能快速实现,但存在严重安全隐患(任意代码执行)且缺乏上下文隔离与类型校验。因此,不推荐在生产环境中使用eval或exec解析用户输入。✅ 推荐方案:使用
pyparsing构建轻量级安全表达式解析器pyparsing是一个成熟、可读性强的语法解析库,适合构建领域特定的小型表达式语言。以下是一个最小可行示例:from pyparsing import infixNotation, opAssoc, Word, alphas, nums, Suppress, oneOf, Group # 定义变量名(如 Bob.age、Jane.age) identifier = Word(alphas, alphas + nums + ".") # 定义数字字面量(支持整数) number = Word(nums).setParseAction(lambda t: int(t[0])) # 构建变量查找函数(模拟你的固定数据表) data = { "Bob.age": 47, "Bill.age": 44, "Jane.age": 36, "Steve.age": 22, } def resolve_var(s): return data.get(s, 0) # 缺失值默认为 0,也可抛异常 # 定义运算符和表达式结构 expr = infixNotation( identifier | number, [ ("==", 2, opAssoc.LEFT, lambda t: t[0][0] == t[0][2]), ("!=", 2, opAssoc.LEFT, lambda t: t[0][0] != t[0][2]), ("<", 2, opAssoc.LEFT, lambda t: t[0][0] < t[0][2]), (">", 2, opAssoc.LEFT, lambda t: t[0][0] > t[0][2]), ("&&", 2, opAssoc.LEFT, lambda t: t[0][0] and t[0][2]), ("||", 2, opAssoc.LEFT, lambda t: t[0][0] or t[0][2]), ], ).setParseAction(lambda t: t[0][0]) # 注入变量解析逻辑(替换 identifier 为实际值) def parse_and_eval(expr_str): try: # 先解析出所有 identifier,再替换为数值 tokens = identifier.searchString(expr_str) for token in tokens: var = token[0] if var in data: expr_str = expr_str.replace(var, str(data[var])) # 再用安全方式求值(仅含数字和运算符) return eval(expr_str.replace("&&", "and").replace("||", "or")) except Exception as e: raise ValueError(f"Invalid expression: {expr_str}") from e # 使用示例 result = parse_and_eval("(Bob.age == Jane.age) || (Bob.age < Bill.age)") print(result) # → False⚠️ 注意事项:
立即学习“Python免费学习笔记(深入)”;
- 永远避免
eval()直接执行原始用户字符串;务必先做白名单变量替换或使用ast.parse()+ 自定义NodeVisitor进行沙箱化求值。- 若需支持更复杂逻辑(函数调用、嵌套括号、优先级等),建议采用
pyparsing或专用规则引擎如simpleeval(专为安全表达式设计)。- JSON 中的
||和&&需映射为 Python 的or/and,或在解析层统一转换。? 总结:动态条件解析不是“是否能做”,而是“如何安全、可维护地做”。优先选择声明式解析(如
pyparsing)或受信沙箱库(如simpleeval),而非绕过安全边界的快捷方式。


















