支持 JSONC/HJSON 的注释保留工具,提供直接的字典式 API,适合配置文件编辑场景。
- 读写都保留注释(
loads→ 修改 →body/full)。 - 通过
sdict处理嵌套结构更方便,等价于 deepmerge + deepdiff + benedict - 提供弱引用有序容器
weakList/OrderedWeakSet。
注释也是数据,就如冯・诺依曼,代码也是数据
Warning
项目当前为 0.2.x,仍在持续演进。
pip install jsonc-sdict本地开发安装:
pip install -e ".[dev]"import hjson
from jsonc_sdict import jsoncDict, Within, NONE
raw = """
{
"a": 1,
"b": 2
}
""".strip()
jc = jsoncDict(raw, loads=hjson.loads, dumps=hjson.dumps)
jc["b"] = 3
jc[Within(NONE, "a")] = "// inserted before a"
print(jc.full)import hjson
from jsonc_sdict import jsoncDict, Within, NONE
raw = """
// header
{
"a": 1, // inline
"b": 2
}
// footer
""".strip()
jc = jsoncDict(raw, loads=hjson.loads, dumps=hjson.dumps)
jc[Within(NONE, "a")] = "// before a"
jc[Within("a")] = {
Within("k", ":"): "/* key slot */",
Within(":", "v"): "/* value slot */",
Within("v", ","): "/* tail slot */",
}
print(jc.full)jsoncDict.comments 使用 Within(...) 记录注释位置。
Within(left, right)表示两个逻辑项之间的注释。Within(key)表示挂在一个 pair 内部槽位上的注释。- 槽位注释使用字典,键为
Within("k", ":")、Within(":", "v")、Within("v", ",")。 Within(NONE, first_key)和Within(last_key, NONE)处理边界注释。
示例:
jc.comments[Within("a", "b")] = "// between a and b"
jc.comments[Within("b")] = {
Within("k", ":"): "/* before colon */",
Within(":", "v"): "/* before value */",
}以下 JSONC 写法非法:
/* // 这是多行注释 */ trailing-text-is-illegalLOG=DEBUG 会把项目 logger 打到 debug 级别。
常用开发流程:
python -m venv .venv
source .venv/bin/activate
pip install -e ".[dev]"
LOG=DEBUG pytest -qjsoncDict.loads()使用 tree-sitter 解析并把注释写入comments。jsoncDict.body把注释回填到序列化结果。jsoncDict.full等于header + body + footer。Within(key)不再存原始字符串,而是存槽位字典,便于精确恢复k:、:v、v,。
sdict会包装映射和可迭代节点;嵌套读取时常拿到sdict视图而不是原始 dict/list。jsoncDict的输出依赖标准的数据/注释修改路径;绕开公开 API 修改底层对象可能导致内部状态不一致。dfs()迭代期间不建议直接改动正在遍历的数据。insert(update, key=...|index=...)的核心是“更新后重排顺序”,不是简单 append。
- 元素必须同时支持
__hash__与弱引用(__weakref__);int/str/list/dict这类内建类型不行。 - 若没有强引用,弱引用对象可能被回收,
WeakList长度会“自动变短”。 WeakList(noRepeat=True)不等同OrderedWeakSet:重复 append/insert 会触发位置移动语义。
可参考的 JSON/JSONC/JSON5 解析项目:
spyoungtech/json-fivetusharsadhwani/json5kitdpranke/pyjson5austinyu/ujson5qvecs/qjson5
其他支持 round-trip 的格式工具:
python-poetry/tomlkitruamel.yaml
在开发本包的时候,刷到了这位老哥的评论:“其中很少有人需要这个”
我觉得这个功能就是,只要有人做出来,就会有很多人会用。
- WeakList: 因为动态语言有GC,子引用父必须是弱引用,考虑到有多个父的极端情况,自制了一个WeakList/OrderedWeakSet
- merge: DeepDiff不支持合并
- dictDict: list[dict]很难合并,而dict很好合并,需要让用户主动选择一个idKey,来方便确定list[dict]内的old_dict,new_dict之间的对应关系
- sdict: 注释在数据中的顺序很重要,继承了OrderedDict重写了纯python的实现。 且因可能存在很深层的数据结构,需要一个方便访问深层结构的api,故参考了benedict(benedict写死了用str做键名),从头重写了大部分逻辑来解除这个限制。
-
microdiff (https://github.com/AsyncBanana/microdiff)
极快、零依赖、现代实现(比 deep-diff 快很多)。
Stars: ~1.5k–2k+
最后更新: 近期活跃(2024-2025仍有更新)
Open issues: 较少(10-20个左右) -
just-diff / deep-object-diff 等也类似,但 microdiff 和 deep-diff 是最直接的竞品。
-
javers (https://github.com/javers/javers)
最成熟、最接近 DeepDiff 的 Java 库,支持任意对象图的深度 diff、变更追踪、快照等,企业级常用。
Stars: ~3.5k–4k+
最后更新: 非常活跃(2025-2026仍在频繁提交)
Open issues: 几十到上百(项目较大) -
java-object-diff (https://github.com/SQiShER/java-object-diff)
专注于对象深度差异。
Stars: ~1k 左右
最后更新: 几年前(基本稳定)
Open issues: 较少
- qri-io/deepdiff (https://github.com/qri-io/deepdiff)
结构化数据深度 diff,目标是近线性时间复杂度。
Stars: 几百(不算特别高)
最后更新: 2021-2023 左右(维护较少)
Open issues: 少量
Go 语言中没有特别火的“DeepDiff 等价物”,很多项目自己实现或用 reflect 简单比较。
- turbodiff (https://github.com/BrightNight-Energy/turbodiff)
明确标榜为 Rust 版的 fast deepdiff,速度优先。
Stars: 几百(新兴)
最后更新: 近期(2024-2025活跃)
Open issues: 很少
Rust 社区更倾向使用 serde + 自定义 diff,或类似工具,但 turbodiff 是最接近的。
-
Clojure → lambdaisland/deep-diff2 (https://github.com/lambdaisland/deep-diff2)
Stars: ~几百
最后更新: 活跃
非常适合 Clojure 数据结构。 -
Swift → DeepDiff (https://github.com/onmyway133/DeepDiff)
同名但不同库,iOS/macOS 常用。
Stars: ~2k+
最后更新: 活跃 -
Julia → DeepDiffs.jl (较小众)
// /* 这依然是单行注释 所以这一行是非法的 */