JSON Logic 规则引擎使用详解

前言

在日常开发中,我们经常需要处理各种业务规则——优惠计算、权限校验、数据过滤、表单验证等。传统做法是硬编码 if/else,但一旦规则频繁变动,维护成本就会急剧上升。

JSON Logic 提供了一种优雅的解决方案:用 JSON 结构来表达逻辑规则,将规则与代码分离,让规则可以动态配置、持久化存储,甚至通过可视化界面编辑。

本文将从基础语法到高级应用,全面介绍 JSON Logic 的使用。

什么是 JSON Logic

JSON Logic 是一种用 JSON 格式表示逻辑规则的规范。它的核心思想是:一切逻辑都可以表示为操作符和参数的组合。

基本格式:

1
{"操作符": [参数列表]}
  • 操作符:一个字符串,表示要执行的操作(如 "+"、"if"、">")
  • 参数列表:一个数组,包含操作符所需的参数
  • 参数可以是字面量、变量引用或嵌套的 JSON Logic 规则

安装

JavaScript

1
npm install json-logic-js
1
const jsonLogic = require('json-logic-js')

Python

1
pip install json-logic
1
import json_logic

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

基础操作符

比较操作符

相等 ==

1
{ "==": [1, 1] }

结果为 true。支持跨类型比较:

1
{ "==": ["1", 1] }

结果为 true。

严格相等 ===

1
{ "===": ["1", 1] }

结果为 false,因为类型不同。

不等 != 和 !==

1
{ "!=": [1, 2] }

结果为 true。

1
{ "!==": ["1", 1] }

结果为 true。

大小比较 >, >=, <, <=

1
{ ">": [5, 3] }

结果为 true。

1
{ "<=": [3, 3] }

结果为 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"] } }

算术操作符

加减乘除

1
{ "+": [1, 2, 3] }

结果为 6。

1
{ "-": [10, 3] }

结果为 7。

1
{ "*": [3, 4] }

结果为 12。

1
{ "/": [10, 3] }

结果为 3.3333...。

取余 %

1
{ "%": [10, 3] }

结果为 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 操作符从输入数据中取值:

1
{ "var": "name" }

从输入 {"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 个字段时返回缺失字段数组。

类型判断

1
{ "is_number": [42] }

结果为 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 })) // true
console.log(jsonLogic.apply(rule, { year: 2025 })) // false

批量验证

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) // [true, true, true]

在 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) # True

规则持久化与动态加载

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
# rules.yaml
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) // "VIP 大客户"

与其他规则引擎对比

特性 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 {
// 验证 JSON 合法性
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 是一个轻量而强大的规则引擎,它的核心优势在于:

  1. 纯 JSON 格式:规则可以序列化存储、动态加载、跨语言使用
  2. 学习成本低:语法直观,熟悉 JSON 的开发者可以快速上手
  3. 表达力强:从简单的比较到复杂的数组操作,覆盖了大部分业务场景
  4. 可扩展:支持自定义操作符,满足特定业务需求
  5. 多语言支持:JavaScript、Python、Java、Go、Ruby、PHP 等主流语言都有实现

适用场景:

  • 业务规则频繁变动的系统
  • 需要规则可视化编辑的管理后台
  • 多租户系统中不同租户有不同的规则配置
  • 需要将规则持久化到数据库的场景

如果你正在寻找一个简单、灵活、跨语言的规则引擎,JSON Logic 值得一试。

参考资源