RLei.RulesJint
1.0.2
dotnet add package RLei.RulesJint --version 1.0.2
NuGet\Install-Package RLei.RulesJint -Version 1.0.2
<PackageReference Include="RLei.RulesJint" Version="1.0.2" />
<PackageVersion Include="RLei.RulesJint" Version="1.0.2" />
<PackageReference Include="RLei.RulesJint" />
paket add RLei.RulesJint --version 1.0.2
#r "nuget: RLei.RulesJint, 1.0.2"
#:package RLei.RulesJint@1.0.2
#addin nuget:?package=RLei.RulesJint&version=1.0.2
#tool nuget:?package=RLei.RulesJint&version=1.0.2
Rules.Jint
一个基于 Jint(.NET 的 JavaScript 解释器)封装的规则引擎库,支持表达式缓存、异步求值和 55 个全局 JS 辅助函数。
功能特性
- 表达式预编译缓存 — 基于
ConcurrentDictionary缓存Prepared<Script>,避免重复解析 - 输入变量自动小写化 — 所有
JObject的属性名在注入引擎时自动转为小写 - 55 个全局辅助函数 — 涵盖类型转换、字符串操作、数学运算、日期时间、集合操作、条件判断、属性检测、工具函数
- 异步求值 — 提供
EvaluateExpressionAsync一个异步 API
快速开始
using Newtonsoft.Json.Linq;
using Rules.Jint;
var factory = new JintEngineFactory();
var expression = "iif(age >= 18, '成人', '未成年')";
var inputs = JObject.FromObject(new { age = 20 });
var result = await factory.EvaluateExpressionAsync(expression, inputs);
Console.WriteLine(result.Result); // 输出: 成人
字符串字面量 vs 变量引用
表达式中的字符串有两种含义,取决于是否使用引号:
| 写法 | 类型 | 说明 | 示例 | 结果 |
|---|---|---|---|---|
'name' |
字面量 | 原始字符串值 | str('name') |
"name" |
name |
变量引用 | 从上下文中查找变量并求值 | str(name) |
变量 name 的值,如 "Alice" |
'user.age' |
字面量 | 原始字符串(含点号) | hasProperty('user.age') |
查根上下文是否有键 user.age |
user.age |
属性访问 | 变量 user 的 age 属性 |
str(user.age) |
变量 user 的 age 值 |
核心原则:
- 引号 → 字符串就是它本身,不会被解析
- 无引号 → Jint 将其视为变量名/表达式,从引擎上下文中求值
如何快速识别
查找引号 → 字符串字面量
// 变量引用的具体值 var inputs = JObject.FromObject(new { name = "Alice" }); await Eval("str(name)"); // "Alice" // 字面量字符串,固定不变 await Eval("str('name')"); // "name"(与上下文无关,始终返回字面字符串)无引号表示变量引用
// 变量引用,可从上下文中获取值 var inputs = JObject.FromObject(new { user = new { name = "Alice" } }); await Eval("str(user.name)"); // "Alice" // 字面量字符串,完全保留原值 await Eval("str('user.name')"); // "user.name"(不解析点号表示法)
实际应用
属性检测示例:
// 字面量用法:检查键名是否存在,而非检查值的存在性
var inputs = JObject.FromObject(new { user = new { name = "Alice", age = 30 } });
await Eval("hasProperty('user.name')", inputs); // true - 检查是否存在 key 'user.name'
await Eval("hasProperty(user, 'name')", inputs); // true - 检查 user 对象是否存在属性 'name'
字符串操作示例:
// 字面量字符串保持原值
await Eval("str('fixed-string')"); // "fixed-string"(与上下文无关)
// 变量引用从上下文中获取值
await Eval("str(fullName)"); // 获取 fullName 变量的实际值
API
核心类
| 类型 | 说明 |
|---|---|
JintEngineFactory |
引擎工厂,提供表达式缓存与异步求值 |
EvaluateRequest |
请求记录:Expression + Inputs |
ExpressionEvaluateResult |
求值结果:Success、ErrorMessage、Result |
JintEngineFactory
// 创建带输入的引擎
Engine CreateEngine(JObject inputs)
// 异步求值(自动缓存表达式)
Task<ExpressionEvaluateResult> EvaluateExpressionAsync(string expression, JObject inputs)
EvaluateRequest
public record EvaluateRequest(string Expression, JObject Inputs);
ExpressionEvaluateResult
| 属性 | 类型 | 说明 |
|---|---|---|
Success |
bool |
求值是否成功 |
ErrorMessage |
string? |
异常时的错误消息 |
Result |
object? |
求值结果 |
全局辅助函数
所有函数名均为 小写开头驼峰 风格,可在 JS 表达式中直接调用。
类型转换
| 函数 | 签名 | 说明 |
|---|---|---|
int |
(val) => int |
转 Int32 |
float |
(val) => double |
转 Double |
str |
(val) => string |
转字符串 |
bool |
(val) => bool |
转 Boolean |
decimal |
(val) => decimal |
转 Decimal |
字符串函数
| 函数 | 签名 | 说明 |
|---|---|---|
toUpperCase |
(s) => string |
转大写 |
toLowerCase |
(s) => string |
转小写 |
trim |
(s) => string |
去除首尾空白 |
contains |
(s, substr) => bool |
是否包含子串 |
startsWith |
(s, prefix) => bool |
是否以指定字符串开头 |
endsWith |
(s, suffix) => bool |
是否以指定字符串结尾 |
substring |
(s, start, length) => string |
截取子串 |
length |
(s) => int |
字符串长度 |
replaceAll |
(s, oldVal, newVal) => string |
全局替换 |
concat |
(a, b) => string |
字符串拼接 |
数学函数
| 函数 | 签名 | 说明 |
|---|---|---|
abs |
(x) => double |
绝对值 |
round |
(x) => double |
四舍五入 |
floor |
(x) => double |
向下取整 |
ceil |
(x) => double |
向上取整 |
max |
(a, b) => double |
返回较大值 |
min |
(a, b) => double |
返回较小值 |
pow |
(x, y) => double |
幂运算 |
sqrt |
(x) => double |
平方根 |
日期时间函数(16 个)
| 函数 | 签名 | 说明 |
|---|---|---|
now |
(format?) => object |
当前本地时间 |
utcNow |
(format?) => object |
当前 UTC 时间 |
today |
(format?) => object |
今日日期 |
parseDate |
(str, format?) => object |
解析日期字符串 |
formatDate |
(dt, format) => string |
格式化日期 |
dateDiff |
(d1, d2) => double |
日期差(天数) |
addDays |
(dt, days) => DateTime |
添加天数 |
addMonths |
(dt, months) => DateTime |
添加月数 |
addYears |
(dt, years) => DateTime |
添加年数 |
year |
(dt) => int |
获取年份 |
month |
(dt) => int |
获取月份 |
day |
(dt) => int |
获取日 |
dayOfWeek |
(dt) => int |
获取星期几 (0=Sunday) |
hour |
(dt) => int |
获取小时 |
minute |
(dt) => int |
获取分钟 |
second |
(dt) => int |
获取秒 |
集合函数
| 函数 | 签名 | 说明 |
|---|---|---|
isArray |
(obj) => bool |
是否为数组 |
count |
(arr) => int |
数组元素个数 |
arrayContains |
(arr, val) => bool |
数组是否包含指定值 |
isEmpty |
(arr) => bool |
数组是否为空 |
first |
(arr) => object? |
第一个元素 |
last |
(arr) => object? |
最后一个元素 |
条件函数
| 函数 | 签名 | 说明 |
|---|---|---|
isNull |
(obj) => bool |
是否为 null 或 DBNull |
isNullOrEmpty |
(obj) => bool |
是否为 null 或空字符串 |
isNullOrWhiteSpace |
(obj) => bool |
是否为 null 或空白字符串 |
coalesce |
(val, default) => object |
返回第一个非 null 值(注:Jint 中会始终执行第二个参数) |
iif |
(cond, trueVal, falseVal) => object |
三元条件 |
属性检测
用于检测对象属性和变量值的函数。
hasProperty — 属性存在检查
// 用法一:检查根上下文中是否存在属性
hasProperty('name') // true / false
hasProperty('user.address.city') // 支持点号分隔的嵌套路径
// 用法二:检查指定对象是否存在属性
hasProperty(obj, 'name') // 指定对象
hasProperty(user, 'address.city') // 指定对象 + 嵌套路径
| 调用方式 | 说明 | 示例 |
|---|---|---|
hasProperty('key') |
检查根上下文是否存在 key |
hasProperty('age') |
hasProperty('parent.child') |
嵌套路径检查,逐层查找 | hasProperty('user.address.city') |
hasProperty(obj, 'key') |
检查指定对象是否存在 key |
hasProperty(user, 'name') |
hasProperty(obj, 'parent.child') |
指定对象 + 嵌套路径 | hasProperty(user, 'address.city') |
嵌套路径规则: 按 . 分隔逐层查找,中间任一节点缺失立即返回 false,不会抛出异常。
// 输入: { user: { name: "Alice", address: { city: "Beijing" } } }
hasProperty('user.name') // true
hasProperty('user.address.city') // true
hasProperty('user.address.zip') // false (zip 不存在)
hasProperty('user.nonexistent') // false (nonexistent 不存在)
hasValue — 值是否存在
检查值是否为非 null、非 DBNull、非空白字符串。
| 签名 | 说明 |
|---|---|
(value) => bool |
值是否存在 |
hasValue(name) // 变量 name 是否有值
hasValue(user.name) // 嵌套属性是否有值
isNotNull — 非 null 检查
检查值是否为非 null、非 DBNull。
| 签名 | 说明 |
|---|---|
(value) => bool |
是否为非 null/非 DBNull |
isNotNull(name) // 变量 name 是否非 null
isNotNull(user) // 变量 user 是否非 null
注意: hasValue 和 isNotNull 接收的是已求值的变量,而非字符串属性名。如果要检查上下文中的某个属性值,请使用不带引号的变量引用:
// ✓ 正确:变量引用,检查 age 的值
hasValue(age)
// ✗ 错误:字符串字面量,只检查字符串 "age" 本身
hasValue('age')
工具函数
| 函数 | 签名 | 说明 |
|---|---|---|
guid |
() => string |
生成新 GUID |
getType |
(obj) => string |
获取类型名称 |
输入变量
JObject 的属性名会自动转为 小写 注入引擎,因此在表达式中应使用小写变量名。
var inputs = JObject.FromObject(new { UserName = "Alice", Age = 30 });
// 在表达式中使用: username、age
var result = await factory.EvaluateExpressionAsync("concat('Hello, ', username) + ' age: ' + str(age)", inputs);
嵌套变量
JObject 的嵌套对象会自动转为平铺的字典结构,可以通过 . 操作符访问:
var inputs = JObject.FromObject(new
{
user = new { name = "Alice", address = new { city = "Beijing" } }
});
// 在表达式中使用:
// user.name → "Alice"
// user.address.city → "Beijing"
var result = await factory.EvaluateExpressionAsync("user.address.city === 'Beijing'", inputs);
完整示例
属性检测与条件判断
var inputs = JObject.FromObject(new
{
user = new { name = " Alice ", age = 25, roles = new[] { "admin", "user" } }
});
var expression = @"
hasProperty(user, 'name') && // 对象是否存在 name 属性
hasValue(user.name) && // 值是否非空
str(toLowerCase(trim(user.name))) === 'alice' && // 字符串处理
user.age >= 18 && // 数字比较
arrayContains(user.roles, 'admin') // 集合包含
";
var result = await factory.EvaluateExpressionAsync(expression, inputs);
// result: true
表达式缓存
JintEngineFactory 内部使用 ConcurrentDictionary 缓存已预编译的表达式,重复表达式不会重复解析。
依赖
- Jint 4.8.0
- Newtonsoft.Json 13.0.4
NuGet
PM> Install-Package RLei.RulesJint
开源协议
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net10.0
- Jint (>= 4.8.0)
- Newtonsoft.Json (>= 13.0.4)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.