Skip to content

oasis-cloud/json-reflector

Repository files navigation

JSON Reflector

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');

API

select(selector: string): JsonMapper

解析源路径 selector,生成 AST 并存入 JsonMapper 实例。

状态:可用

const mapper = select('$..book[1].title');
// mapper.selector → { type: 'Path', body: [...] }

.as(as_selector: string): JsonMapper

解析目标结构 selector,用于定义输出数据的形状。

状态:可用(仅解析,不执行映射)

select('$..book[1].title').as('$.[t]');
// mapper.as_selector → { type: 'Path', body: [...] }

.from(data): R

从输入 JSON 中按源 selector 选取数据,再按 .as() selector 映射为目标结构。

状态:未实现 — 当前仅 console.log AST 并返回 undefined

.filter(data)

按 selector 路径逐步过滤 JSON 数据。

状态:开发中 — 当前仅实现了 root 节点 handler,且遍历逻辑有误(对 AST 对象直接 forEach,应遍历 selector.body)。

.reflector(data)

将选取结果反射/重塑为目标结构。

状态:未实现

Selector 语法

语法参考 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)]

AST 结构

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,展示设计意图。当前均不可运行(执行层未实现)。

1. 按索引选取单条记录并重命名属性

select('$..book[1].[title,price]')
  .as('$.[t,p]')
  .from(data);
// 期望: { t: 'Sword of Honour', p: 12.99 }

2. 过滤后选取

select('$..book[?(@.price<10)].[title,price]')
  .as('$.[t,p]')
  .from(data);
// 期望: 价格低于 10 的书籍,映射为 { t, p }

3. 通配符选取全部匹配项

select('$..book[*].[title,price]')
  .as('$.b.[t,p]')
  .from(data);
// 期望: { b: [{ t, p }, ...] }

4. 根级多属性选取(包装为数组)

select('$.[title,price]')
  .as('$.b.[t,p]')
  .from(data);
// 期望: { b: [{ t, p }] }

5. 根级多属性选取(扁平对象)

select('$.[title,price]')
  .as('$.[t,p]')
  .from(data);
// 期望: { t, p }

6. 递归选取所有匹配属性

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 示例

扩展方向(Roadmap)

P0 — 核心执行层(阻塞 .from() 可用)

  1. 完善 src/lib/filter.ts — 为每种 AST 节点实现 handler:
    • member_child_identifier / member_descendant_identifier
    • subscript_child_numeric / list / slice / wildcard
    • filter_expression / script_expression
  2. 修复 src/lib/mapper.ts .filter() — 遍历 this.selector.body 而非 AST 对象本身
  3. 实现 .from() — 串联 select → filter → as 映射,输出目标结构
  4. 实现 .reflector() — 与 demo 注释中的 reshape 语义对齐

P1 — 解析层完善

  1. Parser 单元测试 — 对照 lexer 用例,断言 AST 结构
  2. 字符串转义 — lexer 双引号字符串支持 \"
  3. Lexer bug 修复] 分支缺少 current++
  4. 移除 parser/mapper 调试日志

P2 — 工程化与体验

  1. TypeScript 类型导出JsonMapper、AST 节点类型、select 返回类型
  2. ESM 构建 — 补充 dual package 或统一入口(demo 引用 index.mjs 但仓库无对应产物)
  3. 完善 package.jsondescriptionkeywordsmain/module/types 字段
  4. 错误信息优化 — parser/lexer 报错带位置与上下文
  5. 性能 — 大 JSON 下的路径缓存、AST 复用

P3 — 高级能力

  1. Filter 表达式引擎 — 解析并求值 ?(@.price<10) 等(可参考 JSONPath 规范)
  2. Script 表达式 — 安全沙箱执行 (expr) 或限制为白名单运算
  3. 自定义函数扩展点 — 允许注册 @.fn() 类扩展
  4. 反向映射 / 双向 reflector — 从新结构写回源 JSON

License

ISC

About

JSON Reflector

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors