首页
/ Nushell nu-json 变更日志全解:从 serde-hjson 分叉到 Hjson 序列化的演进与源码印证

Nushell nu-json 变更日志全解:从 serde-hjson 分叉到 Hjson 序列化的演进与源码印证

2026-09-05 17:10:42作者:韦蓉瑛

本文以 crates/nu-json/CHANGELOG.md 为主线,完整梳理 Nushell 工作区内 nu-json 这个 Hjson 解析/序列化 crate 自 0.22.0 至 0.76.0 的全部变更记录,并结合 Cargo.tomlsrc/lib.rssrc/value.rssrc/ser.rsto json 命令实现 逐条印证这些变更在源码中的落点,帮助读者理解 preserve_order-r/--indent/--tabs 等关键特性的来龙去脉及其底层实现。

一、nu-json 是什么:一个 serde-hjson 的分叉

在展开变更日志之前,先明确这个 crate 的定位。根据 READMEnu-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.rsfmt_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-supportnu-pathserde_jsonfancy-regexpretty_assertionsrstest 这几个测试真正需要的 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

两条记录在当前仓库中都有据可查:

  1. repository 信息已写入 Cargo.tomlrepository = "https://github.com/nushell/nushell/tree/main/crates/nu-json")。
  2. fancy-regex = "0.19.0" 出现在 Cargo.toml 的 dev-dependencies 中,替代了原 serde-hjson 分叉所依赖的 regex crate(该替换主要服务于 Hjson 解析器中需要回溯能力的模式匹配场景)。

0.66.0(2022-07-26):修复大数解析 panic

  • Fixed: Prevents panic when parsing JSON containing large number

这是一条对健壮性很关键的修复:解析包含极大数值(超出 u64/i64 表示范围)的 JSON 时,旧代码会直接 panic,修复后转为返回错误。对照 src/value.rsValue 枚举可以看到,数值被拆分为 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.tomlserde_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.rsto_vec 系列实现可以看到,输出统一写入 Vec<u8> 后再转 Stringto_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 },其 NumCastsrc/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 jsoncargo 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 编码回去;ValueMapfrom_str/to_string 等符号均在 src/lib.rs 中统一再导出,另有 from_slicefrom_readerStreamDeserializer 等流式入口,以及 nu_value 模块(需 nu-protocol feature,供 nu-commandnu-mcp 等 crate 启用)用于与 Nushell 管道值的互转。

五、从变更日志看 crate 的维护模式

通读这份 CHANGELOG 可以归纳出 nu-json 的三条维护主线,且每条都有源码级证据:

  1. 功能演进集中在序列化格式控制:raw(0.42.0)、indent(0.59.1)、tab indentation(0.60.0)三者共同构成了 src/ser.rsto_string / to_string_raw / to_string_with_indent / to_string_with_tab_indentation 四套输出路径,并被 to json 命令分发。
  2. 默认行为的一次性调整:0.28.0 起默认开启 preserve_order,通过 Cargo.toml 的 feature 定义与 src/value.rs 的条件编译 Map 别名实现,保证键序与人类书写顺序一致。
  3. 持续的依赖与 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,从源码结构看,可以推断其后的版本演进信息已随工作区统一版本管理,历史细节以本文件为准。

登录后查看全文
热门项目推荐
相关项目推荐