前言
在日常开发中,我们经常需要处理各种业务规则——优惠计算、权限校验、数据过滤、表单验证等。传统做法是硬编码 if/else,但一旦规则频繁变动,维护成本就会急剧上升。
JSON Logic 提供了一种优雅的解决方案:用 JSON 结构来表达逻辑规则,将规则与代码分离,让规则可以动态配置、持久化存储,甚至通过可视化界面编辑。
本文将从基础语法到高级应用,全面介绍 JSON Logic 的使用。
什么是 JSON Logic
JSON Logic 是一种用 JSON 格式表示逻辑规则的规范。它的核心思想是:一切逻辑都可以表示为操作符和参数的组合。
基本格式:
- 操作符:一个字符串,表示要执行的操作(如
"+"、"if"、">")
- 参数列表:一个数组,包含操作符所需的参数
- 参数可以是字面量、变量引用或嵌套的 JSON Logic 规则
安装
JavaScript
1
| npm install json-logic-js
|
1
| const jsonLogic = require('json-logic-js')
|
Python
Java
1 2 3 4 5
| <dependency> <groupId>io.github.jamsesso</groupId> <artifactId>json-logic-java</artifactId> <version>1.0.8</version> </dependency>
|
Go
1
| go get github.com/diegoholiveira/jsonlogic/v3
|
基础操作符
比较操作符
相等 ==
结果为 true。支持跨类型比较:
结果为 true。
严格相等 ===
结果为 false,因为类型不同。
不等 != 和 !==
结果为 true。
结果为 true。
大小比较 >, >=, <, <=
结果为 true。
结果为 true。
逻辑操作符
与 and
1
| { "and": [{ ">=": [18, 18] }, { "<": [18, 65] }] }
|
结果为 true(年龄在 18-65 之间)。
或 or
1 2 3 4 5 6
| { "or": [ { "==": [{ "var": "role" }, "admin"] }, { "==": [{ "var": "role" }, "manager"] } ] }
|
非 !
1
| { "!": { "==": [{ "var": "status" }, "disabled"] } }
|
算术操作符
加减乘除
结果为 6。
结果为 7。
结果为 12。
结果为 3.3333...。
取余 %
结果为 1。
三元操作 if
1
| { "if": [{ ">=": [{ "var": "age" }, 18] }, "成年人", "未成年人"] }
|
当 age >= 18 时返回 "成年人",否则返回 "未成年人"。
支持多分支:
1 2 3 4 5 6 7 8 9
| { "if": [ { "<": [{ "var": "score" }, 60] }, "不及格", { "<": [{ "var": "score" }, 80] }, "良好", "优秀" ] }
|
变量引用 var
使用 var 操作符从输入数据中取值:
从输入 {"name": "张三"} 中取到 "张三"。
支持路径访问:
1
| { "var": "user.address.city" }
|
从输入 {"user": {"address": {"city": "北京"}}} 中取到 "北京"。
带默认值:
1
| { "var": ["nickname", "匿名用户"] }
|
如果 nickname 不存在,返回 "匿名用户"。
缺失判断 missing / missing_some
1
| { "missing": ["name", "age", "email"] }
|
返回缺失字段的数组。如果所有字段都存在,返回空数组 []。
1
| { "missing_some": [2, ["name", "age", "email"]] }
|
至少缺失 2 个字段时返回缺失字段数组。
类型判断
结果为 true。
1
| { "is_string": ["hello"] }
|
结果为 true。
1
| { "is_array": [[1, 2, 3]] }
|
结果为 true。
1
| { "is_object": [{ "a": 1 }] }
|
结果为 true。
1
| { "is_boolean": [true] }
|
结果为 true。
高级操作符
数组操作
map
1
| { "map": [[1, 2, 3, 4, 5], { "*": [{ "var": "" }, 2] }] }
|
结果为 [2, 4, 6, 8, 10]。{"var": ""} 表示当前元素。
filter
1
| { "filter": [[1, 2, 3, 4, 5, 6], { ">": [{ "var": "" }, 3] }] }
|
结果为 [4, 5, 6]。
reduce
1 2 3 4 5 6 7
| { "reduce": [ [1, 2, 3, 4, 5], { "+": [{ "var": "accumulator" }, { "var": "current" }] }, 0 ] }
|
结果为 15。accumulator 是累加器,current 是当前元素。
all / some
1
| { "all": [[2, 4, 6, 8], { "===": [{ "%": [{ "var": "" }, 2] }, 0] }] }
|
判断是否所有元素都是偶数,结果为 true。
1
| { "some": [[1, 3, 5, 6, 7], { "===": [{ "%": [{ "var": "" }, 2] }, 0] }] }
|
判断是否存在偶数元素,结果为 true。
none
1
| { "none": [[1, 3, 5, 7], { "===": [{ "%": [{ "var": "" }, 2] }, 0] }] }
|
判断是否没有偶数元素,结果为 true。
字符串操作
拼接 cat
1
| { "cat": ["Hello", " ", "World"] }
|
结果为 "Hello World"。
子串 substr
1
| { "substr": ["Hello World", 0, 5] }
|
结果为 "Hello"。
包含 in
1
| { "in": ["World", "Hello World"] }
|
结果为 true。
1
| { "in": ["Hello", ["Hello", "World"]] }
|
在数组中查找,结果为 true。
合并 merge
1 2 3 4 5 6 7
| { "merge": [ [1, 2], [3, 4], [5, 6] ] }
|
结果为 [1, 2, 3, 4, 5, 6]。
其他实用操作符
最大值 max / 最小值 min
1
| { "max": [3, 7, 1, 9, 4] }
|
结果为 9。
1
| { "min": [3, 7, 1, 9, 4] }
|
结果为 1。
计数 count
1
| { "count": [1, 2, 3, 4, 5] }
|
结果为 5。
日志输出 log
1
| { "log": "这条信息会被打印到控制台" }
|
返回原值,同时在控制台打印日志,方便调试。
实际应用场景
场景一:用户权限校验
假设我们需要判断用户是否有权限执行某个操作:
1 2 3 4 5 6 7
| { "and": [ { "in": [{ "var": "user.role" }, ["admin", "editor"]] }, { "==": [{ "var": "user.status" }, "active"] }, { "!": { "var": "user.is_locked" } } ] }
|
输入数据:
1 2 3 4 5 6 7
| { "user": { "role": "editor", "status": "active", "is_locked": false } }
|
结果为 true。
场景二:订单优惠计算
根据订单金额和用户等级计算折扣:
1 2 3 4 5 6 7
| { "if": [ { ">=": [{ "var": "order.amount" }, 1000] }, { "if": [{ "==": [{ "var": "user.level" }, "vip"] }, 0.8, 0.9] }, { "if": [{ "==": [{ "var": "user.level" }, "vip"] }, 0.95, 1.0] } ] }
|
规则说明:
- 订单金额 >= 1000:VIP 打 8 折,普通用户打 9 折
- 订单金额 < 1000:VIP 打 9.5 折,普通用户不打折
场景三:表单验证
验证注册表单的合法性:
1 2 3 4 5 6 7 8 9 10 11 12
| { "and": [ { "and": [ { "!": { "missing": ["username", "email", "password", "age"] } }, { ">=": [{ "var": "age" }, 18] } ] }, { "in": ["@", { "var": "email" }] }, { ">=": [{ "var": { "cat": [{ "var": "password" }, ".length"] } }, 6] } ] }
|
规则说明:
- 必填字段:username、email、password、age
- 年龄必须 >= 18
- email 必须包含
@
- 密码长度 >= 6
场景四:数据过滤
从用户列表中筛选出符合条件的用户:
1 2 3 4 5 6 7 8 9 10 11 12
| { "filter": [ { "var": "users" }, { "and": [ { ">=": [{ "var": "age" }, 18] }, { "in": [{ "var": "city" }, ["北京", "上海", "深圳"]] }, { "==": [{ "var": "is_active" }, true] } ] } ] }
|
输入数据:
1 2 3 4 5 6 7 8
| { "users": [ { "name": "张三", "age": 25, "city": "北京", "is_active": true }, { "name": "李四", "age": 17, "city": "上海", "is_active": true }, { "name": "王五", "age": 30, "city": "广州", "is_active": true }, { "name": "赵六", "age": 22, "city": "深圳", "is_active": false } ] }
|
结果为:
1
| [{ "name": "张三", "age": 25, "city": "北京", "is_active": true }]
|
场景五:动态计算评分
1 2 3 4 5 6 7 8 9 10 11 12
| { "reduce": [ { "var": "scores" }, { "+": [ { "var": "accumulator" }, { "*": [{ "var": "current.score" }, { "var": "current.weight" }] } ] }, 0 ] }
|
输入数据:
1 2 3 4 5 6 7
| { "scores": [ { "score": 90, "weight": 0.3 }, { "score": 80, "weight": 0.3 }, { "score": 95, "weight": 0.4 } ] }
|
计算加权总分:90*0.3 + 80*0.3 + 95*0.4 = 89。
在 JavaScript 中使用
基础用法
1 2 3 4 5 6 7 8 9 10 11 12 13
| const jsonLogic = require('json-logic-js')
const rule = { if: [{ '>=': [{ var: 'age' }, 18] }, '成年人', '未成年人'], }
const data = { age: 25 }
const result = jsonLogic.apply(rule, data) console.log(result)
|
添加自定义操作符
1 2 3 4 5 6 7 8 9 10 11 12 13
| const jsonLogic = require('json-logic-js')
jsonLogic.add_operation('is_leap_year', function (year) { return (year % 4 === 0 && year % 100 !== 0) || year % 400 === 0 })
const rule = { is_leap_year: [{ var: 'year' }], }
console.log(jsonLogic.apply(rule, { year: 2024 })) console.log(jsonLogic.apply(rule, { year: 2025 }))
|
批量验证
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| const jsonLogic = require('json-logic-js')
const rules = [ { '>=': [{ var: 'age' }, 18] }, { in: ['@', { var: 'email' }] }, { '>': [{ var: 'salary' }, 0] }, ]
const data = { age: 25, email: 'test@example.com', salary: 15000, }
const results = rules.map((rule) => jsonLogic.apply(rule, data)) console.log(results)
|
在 Python 中使用
基础用法
1 2 3 4 5 6 7 8 9 10 11
| import json_logic
rule = {"if": [ {">=": [{"var": "age"}, 18]}, "成年人", "未成年人" ]}
data = {"age": 25} result = json_logic.jsonLogic(rule, data) print(result)
|
自定义操作符
1 2 3 4 5 6 7 8 9 10 11 12
| import json_logic
def is_leap_year(year): return (year % 4 == 0 and year % 100 != 0) or (year % 400 == 0)
rule = {"is_leap_year": [{"var": "year"}]} data = {"year": 2024}
result = json_logic.jsonLogic(rule, data, [ ("is_leap_year", is_leap_year) ]) print(result)
|
规则持久化与动态加载
JSON Logic 的最大优势是规则可以序列化为 JSON,存储在数据库或配置文件中,实现动态加载。
存储到数据库
1 2 3 4 5 6 7 8 9 10 11 12
| CREATE TABLE business_rules ( id INT PRIMARY KEY AUTO_INCREMENT, rule_name VARCHAR(100), rule_json JSON, is_active BOOLEAN DEFAULT TRUE, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP );
INSERT INTO business_rules (rule_name, rule_json) VALUES ( 'vip_discount', '{"if": [{"==": [{"var": "user.level"}, "vip"]}, 0.8, 1.0]}' );
|
从配置文件加载
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| discount_rules: seasonal: if: - in: - - 'summer' - 'winter' - - var: season - 0.85 - 1.0 member: if: - '>=': - var: user.member_years - 1 - 0.9 - 0.95
|
规则版本管理
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| const ruleVersions = { v1: { if: [ { '>=': [{ var: 'amount' }, 100] }, { '*': [{ var: 'amount' }, 0.1] }, 0, ], }, v2: { if: [ { '>=': [{ var: 'amount' }, 200] }, { '*': [{ var: 'amount' }, 0.15] }, { if: [ { '>=': [{ var: 'amount' }, 100] }, { '*': [{ var: 'amount' }, 0.1] }, 0, ], }, ], }, }
function calculateDiscount(amount, version = 'v2') { const rule = ruleVersions[version] return jsonLogic.apply(rule, { amount }) }
|
规则可视化编辑
由于 JSON Logic 是纯 JSON 格式,可以轻松构建可视化规则编辑器。以下是一个简单的 HTML 编辑器示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84
| <!DOCTYPE html> <html> <head> <title>JSON Logic 规则编辑器</title> <style> body { font-family: monospace; padding: 20px; background: #f5f5f5; } .container { max-width: 800px; margin: 0 auto; } textarea { width: 100%; height: 200px; font-family: monospace; padding: 10px; } .input-box { width: 100%; height: 150px; font-family: monospace; padding: 10px; } .result { padding: 15px; margin-top: 10px; border-radius: 4px; } .success { background: #d4edda; color: #155724; } .error { background: #f8d7da; color: #721c24; } button { padding: 10px 20px; cursor: pointer; } </style> </head> <body> <div class="container"> <h2>JSON Logic 规则编辑器</h2> <label>规则 (JSON):</label> <textarea id="ruleInput"> { "if": [ {">=": [{"var": "age"}, 18]}, "成年人", "未成年人" ] }</textarea >
<label>输入数据 (JSON):</label> <textarea class="input-box" id="dataInput">{"age": 25}</textarea>
<button onclick="evaluate()">执行规则</button> <div id="result"></div> </div>
<script src="https://cdn.jsdelivr.net/npm/json-logic-js@2/dist/json-logic.min.js"></script> <script> function evaluate() { const resultDiv = document.getElementById('result') try { const rule = JSON.parse(document.getElementById('ruleInput').value) const data = JSON.parse(document.getElementById('dataInput').value) const result = jsonLogic.apply(rule, data) resultDiv.className = 'result success' resultDiv.innerHTML = `<strong>结果:</strong> ${JSON.stringify(result, null, 2)}` } catch (e) { resultDiv.className = 'result error' resultDiv.innerHTML = `<strong>错误:</strong> ${e.message}` } } </script> </body> </html>
|
性能考虑
规则预编译
对于频繁执行的规则,可以预编译以提高性能:
1 2 3 4 5 6 7 8 9 10 11
| const jsonLogic = require('json-logic-js')
const compiledRule = jsonLogic.compile({ if: [{ '>=': [{ var: 'age' }, 18] }, '成年人', '未成年人'], })
console.log(compiledRule({ age: 25 })) console.log(compiledRule({ age: 15 })) console.log(compiledRule({ age: 18 }))
|
规则简化
复杂规则可以拆分为多个子规则,便于管理和缓存:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| const subRules = { isAdult: { '>=': [{ var: 'age' }, 18] }, isVip: { '==': [{ var: 'user.level' }, 'vip'] }, hasHighOrder: { '>=': [{ var: 'order.amount' }, 1000] }, }
const mainRule = { if: [ { and: [{ var: 'isAdult' }, { var: 'isVip' }] }, { if: [{ var: 'hasHighOrder' }, 'VIP 大客户', 'VIP 普通客户'] }, '普通用户', ], }
const data = { age: 25, user: { level: 'vip' }, order: { amount: 2000 } } const subResults = {} for (const [key, rule] of Object.entries(subRules)) { subResults[key] = jsonLogic.apply(rule, data) }
const result = jsonLogic.apply(mainRule, subResults) console.log(result)
|
与其他规则引擎对比
| 特性 |
JSON Logic |
Drools |
Easy Rules |
RuleJS |
| 格式 |
JSON |
DRL/XML |
Java 注解 |
JSON |
| 学习成本 |
低 |
高 |
中 |
低 |
| 跨语言 |
多语言 |
Java 为主 |
Java |
JS 为主 |
| 动态加载 |
原生支持 |
支持 |
需编译 |
支持 |
| 可视化编辑 |
易构建 |
有工具 |
需自建 |
易构建 |
| 性能 |
高 |
高 |
中 |
高 |
| 适用场景 |
简单到中等复杂度 |
复杂规则 |
中等复杂度 |
前端规则 |
最佳实践
1. 规则版本控制
始终为规则添加版本号,便于回溯和灰度发布:
1 2 3 4 5 6 7 8 9 10
| { "rule_version": "2.1.0", "rule": { "if": [ { ">=": [{ "var": "amount" }, 100] }, { "*": [{ "var": "amount" }, 0.1] }, 0 ] } }
|
2. 规则命名与分组
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| { "rules": { "discount": { "description": "折扣计算规则", "priority": 1, "rule": { "if": [ ] } }, "shipping": { "description": "运费计算规则", "priority": 2, "rule": { "if": [ ] } } } }
|
3. 错误处理
1 2 3 4 5 6 7 8 9 10 11 12
| function safeEvaluate(rule, data) { try { if (typeof rule !== 'object' || rule === null) { throw new Error('规则格式错误') } return jsonLogic.apply(rule, data) } catch (error) { console.error('规则执行失败:', error.message) return null } }
|
4. 规则测试
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| const testCases = [ { data: { age: 18 }, expected: '成年人' }, { data: { age: 17 }, expected: '未成年人' }, { data: { age: 0 }, expected: '未成年人' }, { data: { age: 100 }, expected: '成年人' }, ]
function testRule(rule, testCases) { testCases.forEach(({ data, expected }, index) => { const result = jsonLogic.apply(rule, data) const passed = result === expected console.log( `用例 ${index + 1}: ${passed ? 'PASS' : 'FAIL'}`, `输入: ${JSON.stringify(data)}`, `期望: ${expected}`, `实际: ${result}`, ) }) }
|
总结
JSON Logic 是一个轻量而强大的规则引擎,它的核心优势在于:
- 纯 JSON 格式:规则可以序列化存储、动态加载、跨语言使用
- 学习成本低:语法直观,熟悉 JSON 的开发者可以快速上手
- 表达力强:从简单的比较到复杂的数组操作,覆盖了大部分业务场景
- 可扩展:支持自定义操作符,满足特定业务需求
- 多语言支持:JavaScript、Python、Java、Go、Ruby、PHP 等主流语言都有实现
适用场景:
- 业务规则频繁变动的系统
- 需要规则可视化编辑的管理后台
- 多租户系统中不同租户有不同的规则配置
- 需要将规则持久化到数据库的场景
如果你正在寻找一个简单、灵活、跨语言的规则引擎,JSON Logic 值得一试。
参考资源