Project Status: Alpha — 解析层(词法/语法分析)已完成,执行层(数据选取与映射)开发中。
JSON Reflector 是一个轻量级 JSON 数据选取与结构映射库。它采用 JSONPath 风格的路径选择器从 JSON 中定位数据,并通过 .as() 指定目标结构,将选取结果重映射为新形状。
核心用法设计为链式调用:
import { select } from './dist/json-reflector.js';
select('$..book[1].[title,price]')
.as('$.[t,p]')
.from(data);
// 期望输出: { t: 'Sword of Honour', p: 12.99 }注意:
.from()等执行层 API 尚未实现,当前调用仅会解析 selector 并打印 AST,不会返回映射结果。
| 模块 | 状态 | 说明 |
|---|---|---|
| 词法分析(Lexer) | 可用 | 支持 JSONPath 风格 token 识别,含单元测试 |
| 语法分析(Parser) | 可用 | 将 selector 解析为 AST |
select() / .as() |
可用 | 解析并缓存源/目标路径 AST |
.from() |
未实现 | 设计为最终映射入口 |
.filter() |
开发中 | 骨架存在,执行逻辑不完整 |
.reflector() |
未实现 | 空实现 |
npm install| 命令 | 说明 |
|---|---|
npm install |
安装依赖 |
npm run build |
构建,输出 dist/json-reflector.js(CJS) |
npm run dev |
Rollup watch 模式 |
npm test |
运行 Jest 测试(lexer 有覆盖,parser 为占位) |
构建产物入口:
const { select } = require('./dist/json-reflector.js');解析源路径 selector,生成 AST 并存入 JsonMapper 实例。
状态:可用
const mapper = select('$..book[1].title');
// mapper.selector → { type: 'Path', body: [...] }解析目标结构 selector,用于定义输出数据的形状。
状态:可用(仅解析,不执行映射)
select('$..book[1].title').as('$.[t]');
// mapper.as_selector → { type: 'Path', body: [...] }从输入 JSON 中按源 selector 选取数据,再按 .as() selector 映射为目标结构。
状态:未实现 — 当前仅 console.log AST 并返回 undefined。
按 selector 路径逐步过滤 JSON 数据。
状态:开发中 — 当前仅实现了 root 节点 handler,且遍历逻辑有误(对 AST 对象直接 forEach,应遍历 selector.body)。
将选取结果反射/重塑为目标结构。
状态:未实现
语法参考 JSONPath 并做了扩展,支持以下形式:
| 语法 | 含义 | 解析 | 执行 |
|---|---|---|---|
$ |
根节点 | 已支持 | 部分支持 |
.field |
子成员访问(标识符) | 已支持 | 未实现 |
.123 |
子成员访问(数字键) | 已支持 | 未实现 |
..field |
递归下降(所有后代) | 已支持 | 未实现 |
..[n] |
递归下降 + 下标 | 已支持 | 未实现 |
[n] |
数组/对象下标 | 已支持 | 未实现 |
['key'] / ["key"] |
字符串键下标 | 已支持 | 未实现 |
[1,2] |
多下标列表 | 已支持 | 未实现 |
[start:end:step] |
切片(如 [1:2:3]、[:1:2]) |
已支持 | 未实现 |
[*] |
通配符 | 已支持 | 未实现 |
.[a,b] |
多属性选取 | 已支持 | 未实现 |
[?(@.price<10)] |
过滤表达式 | 已解析 | 未执行 |
[(expr)] |
脚本表达式 | 已解析 | 未执行 |
以下 selector 均可被正确 tokenize(参见 test/lexer/index.test.js):
$
$.a
$..1
$..[1,2]
$..["1","2"]
$..[1:2:3]
$..[:1:2]
$..[?(@price<1)]
$..[(@price<1)]
Parser 返回如下结构,供贡献者理解内部表示:
{
type: 'Path',
body: Array<{
scope?: 'child' | 'descendant',
operation?: 'member' | 'subscript',
expression: {
type: string, // root | identifier | wildcard | numeric | string_literal | list | slice | filter_expression | script_expression
value: any
}
}>
}以下示例来自 demo/index.ts,展示设计意图。当前均不可运行(执行层未实现)。
select('$..book[1].[title,price]')
.as('$.[t,p]')
.from(data);
// 期望: { t: 'Sword of Honour', p: 12.99 }select('$..book[?(@.price<10)].[title,price]')
.as('$.[t,p]')
.from(data);
// 期望: 价格低于 10 的书籍,映射为 { t, p }select('$..book[*].[title,price]')
.as('$.b.[t,p]')
.from(data);
// 期望: { b: [{ t, p }, ...] }select('$.[title,price]')
.as('$.b.[t,p]')
.from(data);
// 期望: { b: [{ t, p }] }select('$.[title,price]')
.as('$.[t,p]')
.from(data);
// 期望: { t, p }select('$..[title,price]')
.as('$.[t,p]')
.from(data);
// 期望: [{ t, p }, ...]- 双引号字符串不支持转义字符(如
\"),lexer 中有待办标记 - Parser 内含调试
console.log,不适合生产环境 - Lexer 在处理
]时存在潜在 bug(未递增读取位置) - 无 TypeScript 类型声明文件导出
- 仅提供 CJS 构建产物,无 ESM 输出
json-reflector/
├── src/
│ ├── index.ts # 公共入口,导出 select()
│ ├── lib/
│ │ ├── lexer.ts # 词法分析
│ │ ├── parser.ts # 语法分析 → AST
│ │ ├── mapper.ts # JsonMapper 链式 API
│ │ ├── filter.ts # AST 节点 → 数据过滤
│ │ └── token.ts # Token 工厂
│ └── utils/
│ ├── invariant.ts
│ └── is_string.ts
├── demo/
│ └── index.ts # 预期用法示例(注释)
├── test/
│ ├── lexer/ # Lexer 单元测试(较完整)
│ ├── parser/ # Parser 测试(占位)
│ └── data/store.json # 测试数据
├── dist/ # 构建输出
└── rollup.config.js
# 安装依赖
npm install
# 构建
npm run build
# 运行测试
npm test- Lexer 测试:覆盖
$、标识符、数字、./..、列表、切片、过滤/脚本表达式等 token 识别 - Parser 测试:当前为占位测试,尚未断言 AST 结构
- 测试数据:
test/data/store.json提供标准 bookstore 示例
- 完善
src/lib/filter.ts— 为每种 AST 节点实现 handler:member_child_identifier/member_descendant_identifiersubscript_child_numeric/list/slice/wildcardfilter_expression/script_expression
- 修复
src/lib/mapper.ts.filter()— 遍历this.selector.body而非 AST 对象本身 - 实现
.from()— 串联 select → filter → as 映射,输出目标结构 - 实现
.reflector()— 与 demo 注释中的 reshape 语义对齐
- Parser 单元测试 — 对照 lexer 用例,断言 AST 结构
- 字符串转义 — lexer 双引号字符串支持
\"等 - Lexer bug 修复 —
]分支缺少current++ - 移除 parser/mapper 调试日志
- TypeScript 类型导出 —
JsonMapper、AST 节点类型、select返回类型 - ESM 构建 — 补充 dual package 或统一入口(demo 引用
index.mjs但仓库无对应产物) - 完善 package.json —
description、keywords、main/module/types字段 - 错误信息优化 — parser/lexer 报错带位置与上下文
- 性能 — 大 JSON 下的路径缓存、AST 复用
- Filter 表达式引擎 — 解析并求值
?(@.price<10)等(可参考 JSONPath 规范) - Script 表达式 — 安全沙箱执行
(expr)或限制为白名单运算 - 自定义函数扩展点 — 允许注册
@.fn()类扩展 - 反向映射 / 双向 reflector — 从新结构写回源 JSON
ISC