Nushell nu-json 变更日志全解:从 serde-hjson 分叉到 Hjson 序列化的演进与源码印证
本文以 crates/nu-json/CHANGELOG.md 为主线,完整梳理 Nushell 工作区内 nu-json 这个 Hjson 解析/序列化 crate 自 0.22.0 至 0.76.0 的全部变更记录,并结合 Cargo.toml、src/lib.rs、src/value.rs、src/ser.rs 与 to json 命令实现 逐条印证这些变更在源码中的落点,帮助读者理解 preserve_order、-r/--indent/--tabs 等关键特性的来龙去脉及其底层实现。
一、nu-json 是什么:一个 serde-hjson 的分叉
在展开变更日志之前,先明确这个 crate 的定位。根据 README,nu-json 是 crates.io 上 serde-hjson 的分叉(fork),是 Hjson(Human JSON,允许注释、无引号键、尾逗号等人读友好扩展)的 Rust 实现,构建在 Serde 之上。Cargo.toml 中的 description = "Fork of serde-hjson" 也印证了这一点。而 README 明确写道:“The changes made to this crate are kept in [CHANGELOG]”,即这份变更日志就是 Nushell 团队在 fork 之后所做全部改动的权威记录。
从 CHANGELOG.md 的头部可以看到,该文件遵循 Keep a Changelog 格式并遵守语义化版本(Semantic Versioning),文件内还保留了一段 HTML 注释形式的模板,规定了 Added / Changed / Deprecated / Removed / Fixed / Security 六个标准小节,供后续发布时填写。
二、演进时间线:逐条解读变更记录
以下按版本从新到旧完整覆盖 CHANGELOG 中的每一条记录,并结合当前仓库源码说明每条变更的实际含义。
0.76.0(2023-02-21):禁用本 crate 的自动基准测试
- Changed: Disable auto-benchmark harness for this crate
这条变更在当前 Cargo.toml 中有直接对应:
[lib]
bench = false
bench = false 让 Cargo 不再为该库自动生成 bench.rs 入口,从源头上关闭了自动 benchmark harness,避免了 crate 被单独 cargo bench 时的干扰。
0.75.0(2023-01-31):代码整洁化与 clippy 收紧
- Changed: Removing unnecessary comments from the code
- Changed: Use variable names directly in the format strings
- Changed: Apply more recent/nightly clippy lints
“Use variable names directly in the format strings”指的是 clippy 中 uninlined_format_args 一类的 lint:将 format!("{}", x) 统一写成 format!("{x}")。在当前 src/ser.rs 的 fmt_small 函数中可以看到这种写法已经落地,例如 format!("{value:e}")。同时 Cargo.toml 末尾声明了 [lints] workspace = true,说明该 crate 直接继承工作区统一的 lint 配置,这正是“应用更新的 clippy lint”能够持续生效的机制。
0.73.0(2022-12-20):精简依赖与 once_cell 替换
- Changed: Remove unused dev-dependencies
- Changed: The lazy_static crate has been replaced by once_cell
从当前 Cargo.toml 可以看到 dev-dependencies 只保留了 nu-test-support、nu-path、serde_json、fancy-regex、pretty_assertions、rstest 这几个测试真正需要的 crate,lazy_static 已从依赖中消失(由 once_cell 承接其惰性初始化职责)。
0.71.0(2022-11-29 / 2022-11-08):JSON 解析修复与 lint 清扫
- Fixed: Fixed json parsing
- Changed: Run a round of clippy --fix to fix a ton of lints
这两条属于典型的稳定性维护:先修复解析缺陷,再批量跑 clippy --fix 清扫 lint。它们保证了 from_str/from_slice 等入口(在 src/lib.rs 中统一导出)的行为正确与代码风格一致。
0.67.0(2022-08-16):fancy-regex 替换 regex
- Added: Add repository info to all workspace crates
- Changed: Replace the regex crate with the fancy-regex crate
两条记录在当前仓库中都有据可查:
repository信息已写入 Cargo.toml(repository = "https://github.com/nushell/nushell/tree/main/crates/nu-json")。fancy-regex = "0.19.0"出现在 Cargo.toml 的 dev-dependencies 中,替代了原serde-hjson分叉所依赖的regexcrate(该替换主要服务于 Hjson 解析器中需要回溯能力的模式匹配场景)。
0.66.0(2022-07-26):修复大数解析 panic
- Fixed: Prevents panic when parsing JSON containing large number
这是一条对健壮性很关键的修复:解析包含极大数值(超出 u64/i64 表示范围)的 JSON 时,旧代码会直接 panic,修复后转为返回错误。对照 src/value.rs 的 Value 枚举可以看到,数值被拆分为 I64(i64)、U64(u64)、F64(f64) 三类,边界处理正是围绕这种定宽整数表示展开的。
0.64.0(2022-06-15):适配 beta/nightly clippy
- Changed: Address lints from clippy for beta/nightly
与 0.75.0 类似,属于编译器演进驱动的例行维护,确保 crate 在较新的 Rust 工具链下保持零警告。
0.60.1(2022-03-27):serde_json 版本对齐
- Changed: Align all of the serde_json crates to the same version(
serde_json = "0.1.39"统一为serde_json = "0.1")
该条目记录的是将各处写死的 serde_json 版本要求收敛到同一版本区间,避免同一个依赖树中出现多个 serde_json 小版本。当前 Cargo.toml 中 serde_json 已通过 workspace = true 统一由工作区管理版本,正是这种对齐思路的延续。
0.60.0(2022-03-22):新增制表符缩进选项
- Added: Adds tab indentation option for JSON files.
- Changed: Changing the name of the parent project: 'The Nu Project' → 'The Nushell Project'
这是功能层面的重要一记。在当前 src/ser.rs 中可以看到对应的公开 API:
pub fn to_writer_with_tab_indentation<W, T>(writer: &mut W, value: &T, tabs: usize) -> Result<()>
pub fn to_string_with_tab_indentation<T>(value: &T, tabs: usize) -> Result<String>
实现上只是构造 "\t".repeat(tabs) 作为缩进串,再交给带缩进的 Serializer::with_indent。它被 to json 命令 的 --tabs/-t 参数直接消费,例如 [1 2 3] | to json -t 2 会用两级制表符缩进输出。
0.59.1(2022-03-02):indent 标志首版与转义修复
- Added: Add indent flag to json (first draft)
- Fixed: Fix to json escape logic
- Changed: Update this cargo crate to edition 2021
- Changed: Strip trailing whitespace in files
“indent flag first draft”是上一条 tab 缩进的前身,对应 src/ser.rs 中的 to_writer_with_indent / to_string_with_indent(以空格数作为缩进宽度)。当前 to json 命令 中的 --indent/-i 参数即源于此,官方示例 [Joe Bob Sam] | to json --indent 4 会输出 4 空格缩进的 JSON 文本。此外,crate 从 edition 2018 迁移到 2021,也为此后的语法与依赖管理现代化铺了路。
0.42.0(2021-12-28):raw 标志与 datetime 序列化修复
- Fixed: fix issue #559: to json -r serializes datetime without spaces
- Changed: add in a raw flag in the command to json
这是 nu-json 历史上最有用户感知度的一条:为 to json 增加了 --raw/-r 开关,并在 src/ser.rs 中实现了 to_string_raw:
/// Encode the specified struct into a Hjson `String` buffer.
/// And remove all whitespace
pub fn to_string_raw<T>(value: &T) -> Result<String>
where
T: ser::Serialize,
{
let result = serde_json::to_string(value);
...
}
值得注意的是其实现细节:to_string_raw 并不走 Hjson 的 Serializer,而是直接委托 serde_json::to_string 产生严格紧凑的 JSON 字符串(无空格、无 Hjson 扩展),再包装成本 crate 的错误类型。to json 命令 的分发逻辑也印证了这一点:优先 raw,其次 tabs,再次 indent,最后回落到默认 to_string。修复 #559 则解决了 raw 模式下 datetime 值缺少空格分隔的问题。
0.41.0(2021-12-07):消除序列化时的多余分配
- Changed: avoid unnecessary allocation (serialization)
从当前 src/ser.rs 的 to_vec 系列实现可以看到,输出统一写入 Vec<u8> 后再转 String,to_string/to_string_with_indent 等函数都薄薄地包一层,避免中间多次字符串拼接。
0.37.0(2021-09-14)与 0.31.0(2021-05-11):重构与 clippy 修复
- Changed: Add general refactorings(0.37.0)
- Fixed: Clippy fixes for new Rust version(0.31.0)
0.29.x(2021-04-06 / 2021-03-30):拼写与 Rust 1.51 警告修复
- Fixed: Fix typos and capitalization of "Unicode"(0.29.2)
- Fixed: Fix warnings for Rust 1.51(0.29.0)
0.28.0(2021-03-09):默认保留键序
- Changed: Preserve order when serializing/deserialize json by default.
这条是整个 crate 行为特征的分水岭,也是 README 中 Hjson 语义的关键一环。在当前 Cargo.toml 中可以看到其最终形态:
[features]
preserve_order = ["linked-hash-map", "linked-hash-map/serde_impl", "serde_json/preserve_order"]
default = ["preserve_order"]
即在 src/value.rs 中,Map<K, V> 的类型别名按 feature 条件编译:
#[cfg(not(feature = "preserve_order"))]
pub type Map<K, V> = BTreeMap<K, V>;
#[cfg(feature = "preserve_order")]
pub type Map<K, V> = LinkedHashMap<K, V>;
默认开启 preserve_order 后,对象使用 linked_hash_map::LinkedHashMap 而非 BTreeMap,序列化/反序列化时键的首次出现顺序得以保留,而不是被字母序打乱。对于 shell 场景(例如 to json 后再 open 一个配置文件),这一点对“人类可读”的 diff 体验至关重要。
0.25.x(2021-01):依赖升级与 Clippy 修复
- Changed: Update num-traits requirement from 0.1.32 to 0.2.14(0.25.2)
- Fixed: Rust 1.49 Clippy Fixes(0.25.0)
num-traits 的升级在当前 Cargo.toml 中体现为 num-traits = { workspace = true },其 NumCast 在 src/value.rs 中被导入,服务于 I64/U64/F64 之间的数值转换。
0.22.0(2020-11-22):crate 的诞生
- Changed: Fork of serde-hjson
- Changed: The crate added to the 'Nu Project'
- Changed: Added Cargo.toml
- Changed: LICENSE file added
- Changed: Bump version to 0.22 according to the parent project
这是 nu-json 的起点:从 serde-hjson 分叉而来,以独立 crate 的身份加入 Nushell 工作区,补齐 Cargo.toml 与 LICENSE,并将版本号对齐父项目(当时的 0.22)。从 0.22.0 到 0.76.0 的每一次发布,crate 版本都与 Nushell 主项目版本号保持同步,这也是 CHANGELOG 中“Bump version according to the parent project”机制的延续。
三、变更记录速查表
| 版本 | 日期 | 类别 | 变更要点 |
|---|---|---|---|
| 0.76.0 | 2023-02-21 | Changed | 禁用本 crate 的自动 benchmark harness |
| 0.75.0 | 2023-01-31 | Changed | 清理冗余注释、format 字符串内联变量名、应用更新 clippy lint |
| 0.73.0 | 2022-12-20 | Changed | 移除未用 dev-dependencies;lazy_static 替换为 once_cell |
| 0.71.0 | 2022-11-29 | Fixed | 修复 JSON 解析 |
| 0.71.0 | 2022-11-08 | Changed | 一轮 clippy --fix 清扫大量 lint |
| 0.67.0 | 2022-08-16 | Added/Changed | 所有工作区 crate 增加 repository 信息;regex 替换为 fancy-regex |
| 0.66.0 | 2022-07-26 | Fixed | 修复解析含大数 JSON 时的 panic |
| 0.64.0 | 2022-06-15 | Changed | 处理 beta/nightly clippy lint |
| 0.60.1 | 2022-03-27 | Changed | serde_json 版本统一对齐 |
| 0.60.0 | 2022-03-22 | Added/Changed | 新增 tab 缩进选项;父项目更名 'The Nushell Project' |
| 0.59.1 | 2022-03-02 | Added/Fixed/Changed | indent 标志首版;修复 json 转义逻辑;迁移 edition 2021;去除文件尾随空白 |
| 0.42.0 | 2021-12-28 | Fixed/Changed | 修复 #559(raw 序列化 datetime 无空格);to json 新增 raw 标志 |
| 0.41.0 | 2021-12-07 | Changed | 消除序列化时的不必要分配 |
| 0.37.0 | 2021-09-14 | Changed | 通用重构 |
| 0.31.0 | 2021-05-11 | Fixed | 新版 Rust 的 Clippy 修复 |
| 0.29.2 | 2021-04-06 | Fixed | 修正 "Unicode" 拼写与大小写 |
| 0.29.0 | 2021-03-30 | Fixed | 修复 Rust 1.51 警告 |
| 0.28.0 | 2021-03-09 | Changed | 默认保留 JSON 序列化/反序列化键序 |
| 0.27.0 | 2021-02-16 | Fixed | 修复最新 clippy 警告 |
| 0.25.2 | 2021-01-11 | Changed | num-traits 从 0.1.32 升至 0.2.14 |
| 0.25.0 | 2021-01-05 | Fixed | Rust 1.49 Clippy 修复 |
| 0.22.0 | 2020-11-22 | Changed | 分叉 serde-hjson;加入 Nu 项目;新增 Cargo.toml 与 LICENSE;版本对齐父项目 |
四、实战:这些变更如何落到 Nushell 命令上
CHANGELOG 中的功能条目最终都服务于 Nushell 的 to json 命令。当前 crates/nu-command/src/formats/to/json.rs 的签名完整暴露了四个选项,与 CHANGELOG 时间线一一对应:
Signature::build("to json")
.switch("raw", "Remove all of the whitespace and trailing line ending.", Some('r'))
.named("indent", SyntaxShape::Number, "Specify indentation width.", Some('i'))
.named("tabs", SyntaxShape::Number, "Specify indentation tab quantity.", Some('t'))
.switch("serialize", "Serialize nushell types that cannot be deserialized.", Some('s'))
官方内置示例(可用于 help to json 或 cargo test 验证行为):
# 默认 2 空格缩进(0.59.1 引入的 indent 能力)
[a b c] | to json
# 4 空格缩进
[Joe Bob Sam] | to json --indent 4
# raw 紧凑输出(0.42.0 引入的 raw 能力,底层走 to_string_raw)
[1 2 3] | to json -r
# => [1,2,3]
在 Rust 侧直接使用 crate 的话,README 给出了最小可用示例:通过 nu_json::from_str 解析带注释、无引号键的 Hjson 文本为 Map<String, Value>,修改后再用 nu_json::to_string 编码回去;Value、Map 与 from_str/to_string 等符号均在 src/lib.rs 中统一再导出,另有 from_slice、from_reader、StreamDeserializer 等流式入口,以及 nu_value 模块(需 nu-protocol feature,供 nu-command、nu-mcp 等 crate 启用)用于与 Nushell 管道值的互转。
五、从变更日志看 crate 的维护模式
通读这份 CHANGELOG 可以归纳出 nu-json 的三条维护主线,且每条都有源码级证据:
- 功能演进集中在序列化格式控制:raw(0.42.0)、indent(0.59.1)、tab indentation(0.60.0)三者共同构成了 src/ser.rs 中
to_string/to_string_raw/to_string_with_indent/to_string_with_tab_indentation四套输出路径,并被to json命令分发。 - 默认行为的一次性调整:0.28.0 起默认开启
preserve_order,通过 Cargo.toml 的 feature 定义与 src/value.rs 的条件编译Map别名实现,保证键序与人类书写顺序一致。 - 持续的依赖与 lint 卫生:fancy-regex 替换(0.67.0)、once_cell 替换(0.73.0)、clippy 多轮清扫(0.31.0 / 0.64.0 / 0.75.0)以及 0.76.0 的
bench = false,都体现了跟随上游 serde 生态与 Nushell 工作区统一 lint 策略的维护节奏。
需要注意的是,CHANGELOG 最后一条记录停在 0.76.0(2023-02-21),而 Cargo.toml 中 crate 版本已改为 version.workspace = true,从源码结构看,可以推断其后的版本演进信息已随工作区统一版本管理,历史细节以本文件为准。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0623
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00