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
]
}

结果为 15accumulator 是累加器,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 值得一试。

参考资源