jsonuri
v4.0.1
Published
Use URI path to get or set data
Downloads
1,387
Readme
jsonuri
English | 简体中文
一个轻量级、强大的 JSON 对象操作工具库,使用 linux 路径风格表达式来访问和修改嵌套的 JSON 数据,能快速访问数据的上级结构。
安装
npm i jsonuri
# 或 pnpm add jsonuri / yarn add jsonuri使用方式:
// 推荐按需导入(便于 tree-shaking)
import {
get,
set,
rm,
insert,
mv,
swap,
up,
down,
normalizeUri,
parseUri,
parent,
parents,
isCircular,
walk,
walkTopDownDFS,
walkTopDownBFS,
walkBottomUpDFS,
walkBottomUpBFS,
} from 'jsonuri'
// 或整体导入
import * as jsonuri from 'jsonuri'路径规则
分隔符:
/(如menu/popup/menuitem/0/value)当前层:
.;父级:..转义斜杠:在键名内使用
\/,如a\/b/c解析为段["a/b", "c"]数组快捷段(仅
get中解析,且要求当前值为数组):@first(首元素)@last或@length-1(末元素)@length-N(倒数第N个,N≥ 0 且整数)
简单键 与 复杂路径 的判定: 传入的
path若是不含/的字符串或非负整数索引,则按“简单键/索引”处理;否则按“复杂路径”解析。
若路径解析过程中父级超出根(例如过多的
..),解析将判定为非法并返回空结果。
示例数据
{
"menu": {
"id": 123,
"list": [0, 1, 2, 3, 4],
"popup": {
"menuitem": [
{ "value": "New", "onclick": "CreateNewDoc()" },
{ "value": "Open", "onclick": "OpenDoc()" },
{ "value": "Close", "onclick": "CloseDoc()" }
]
}
}
}以下示例假设变量 data 为上面 JSON。
API
get(data, path)
按路径读取值。
get(data, 'menu/id') // 123
get(data, 'menu/popup/menuitem/0/value') // "New"
get(data, 'menu/popup/menuitem/0/value/..') // { value: "New", onclick: "CreateNewDoc()" }
get(data, 'menu/popup/menuitem/@last/value') // "Close"
get([10, 11, 12], '@length-2') // 11
get(data, '/') // 整个 data若中途遇到
null/undefined,直接返回该值;非法路径返回undefined。
set(data, path, value)
按路径写入。中间缺失层会被创建为对象。
对数组会使用 splice 写入;写入键名为 length 时会裁剪/扩展数组长度。
set(data, 'menu/id', 789)
get(data, 'menu/id') // 789
set(data, 'menu/list/7', 999) // 自动扩容至索引 7
get(data, 'menu/list') // [0,1,2,3,4, undefined, undefined, 999]
// 直接改 length(简单键)
const arr = [0, 1, 2, 3]
set(arr, 'length', 2)
arr // [0,1]保护键名:
__proto__/prototype/constructor会被跳过(不会创建/写入)。
rm(data, path)
按路径删除值/项(对象用 delete,数组用 splice)。
rm(data, 'menu/id')
get(data, 'menu/id') // undefined
rm(data, 'menu/list/1')
get(data, 'menu/list') // [0,2,3,4]insert(data, path, value, direction = 'before')
仅对数组 生效:在 path 所指索引之前/之后插入一个元素。
direction 仅支持 'before' | 'after'。
// 在第 0 项之前插入 9999
insert(data, 'menu/list/0', 9999, 'before') // [9999,0,1,2,3,4]
// 在第 2 项之后插入 -1
insert(data, 'menu/list/2', -1, 'after') // [9999,0,1,2,-1,3,4]若索引
< 0或> length会抛出 “Index Out of Bounds”。 源码中出现了'inside'分支,但仅打印 TODO;不提供该能力。 文本常量包含'append'字样,但实现未支持'append'。
mv(data, fromPath, toPath, direction = 'before')
移动节点。常见用法为数组元素跨/同层级移动到目标索引前后。
set(data, 'menu/list', [0, 1, 2, 3, 4])
mv(data, 'menu/list/0', 'menu/list/3') // 默认 'before'
get(data, 'menu/list') // [1,2,3,0,4]
set(data, 'menu/list', [0, 1, 2, 3, 4])
mv(data, 'menu/list/0', 'menu/list/3', 'before')
get(data, 'menu/list') // [1,2,0,3,4]当 toPath 指向的父容器为数组以外的对象/值时:
- 若
get(data, toPath)不是对象/函数,将抛错(primitive values)。 - 否则会把
fromPath对应的值放到新路径toPath + '/' + fromPath下,然后删除fromPath。 (这是源码的实际行为,注意新键名/嵌套可能包含fromPath的多段。)
swap(data, pathA, pathB)
交换两个路径的值(不存在的源会记录错误并保持原值)。
set(data, 'menu/list', [0, 1, 2, 3, 4])
swap(data, 'menu/list/0', 'menu/list/4')
get(data, 'menu/list') // [4,1,2,3,0]up(data, path, step = 1) / down(data, path, step = 1)
将数组中某个元素上移/下移 step 步;越界将被钳制到边界。
set(data, 'menu/list', [0, 1, 2, 3, 4])
up(data, 'menu/list/3') // 上移 1
get(data, 'menu/list') // [0,1,3,2,4]
down(data, 'menu/list/1', 2) // 下移 2
get(data, 'menu/list') // [0,3,2,1,4]normalizeUri(...parts)
组合并规范化路径片段(处理 .、.. 和数组/嵌套传参)。
normalizeUri('a', 'b') // 'a/b'
normalizeUri(['a', 'b', '../'], 'c') // 'a/c'相关辅助:
parseUri(input)→ 返回段数组(含转义与..处理)parent(path)→ 返回父路径或nullparents(path)→ 返回从近到远所有父路径(不含自身),如a/b/c→['a/b','a']
遍历(含中止能力)
所有遍历回调签名一致:(value, key, parent, { uri, stop }) => any | Promise<any>
walk/walkTopDownDFS(同实现) 自顶向下 DFS-先序;不会在根节点调用回调,只对根的子节点及以下调用。walkTopDownBFS自顶向下 BFS-先序;会在根节点调用回调。walkBottomUpDFS自底向上 DFS-后序;会在根节点调用回调(最后)。walkBottomUpBFS先按 BFS 收集,再自底向上回调;会在根节点调用回调(最后)。
示例(DFS-先序):
await walk({ a: { a1: 'x' } }, (val, key, parent, { uri, stop }) => {
// 回调不会在根 {a:{a1:'x'}} 触发
// 第一次:val={a1:'x'}, key='a', uri='a'
// 第二次:val='x', key='a1', uri='a/a1'
if (uri === 'a/a1') stop() // 可中止后续遍历
})若输入对象存在循环引用,四种遍历都会在开始前抛出错误。可先用
isCircular(obj)预检。
isCircular(obj)
检测循环引用。
isCircular({}) // false
const a: any = {}
set(a, '/b/c', a)
isCircular(a) // true异常与边界
- 非法路径或容器类型不匹配(如对非数组执行
insert/up/down)会抛错或静默返回。 insert索引越界抛出 “the Index Out of Bounds”。set(arr, 'length', n)要求n为非负整数,否则抛出 “value: n is not a natural number”。mv到非对象目标且toPath指向原始类型时抛错(primitive values)。- 写入时会跳过键名
__proto__/prototype/constructor。
许可证
MIT
