npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

jsonuri

v4.0.1

Published

Use URI path to get or set data

Downloads

1,387

Readme

jsonuri

English | 简体中文

一个轻量级、强大的 JSON 对象操作工具库,使用 linux 路径风格表达式来访问和修改嵌套的 JSON 数据,能快速访问数据的上级结构。

npm codecov contributors LICENSE Size

安装

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) → 返回父路径或 null
  • parents(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