93 lines
3.3 KiB
Rust
93 lines
3.3 KiB
Rust
//! JSON 序列化器——把 [`JsonValue`] 渲染回字符串。
|
||
//! JSON serializer — renders a [`JsonValue`] back to a String.
|
||
|
||
use crate::value::JsonValue;
|
||
use std::fmt::Write;
|
||
|
||
/// 将 [`JsonValue`] 序列化为紧凑的 JSON 字符串。
|
||
/// Serialize a [`JsonValue`] to a compact JSON string.
|
||
pub fn to_string(value: &JsonValue) -> String {
|
||
let mut out = String::new();
|
||
write_value(value, &mut out);
|
||
out
|
||
}
|
||
|
||
/// 把一个值写入 `out`(递归处理数组与对象)。
|
||
/// Write a value into `out` (recursing into arrays and objects).
|
||
fn write_value(value: &JsonValue, out: &mut String) {
|
||
match value {
|
||
JsonValue::Null => out.push_str("null"),
|
||
JsonValue::Bool(true) => out.push_str("true"),
|
||
JsonValue::Bool(false) => out.push_str("false"),
|
||
JsonValue::Num(n) => write_number(*n, out),
|
||
JsonValue::Str(s) => write_string(s, out),
|
||
JsonValue::Arr(items) => {
|
||
out.push('[');
|
||
for (i, item) in items.iter().enumerate() {
|
||
if i > 0 {
|
||
out.push(',');
|
||
}
|
||
write_value(item, out);
|
||
}
|
||
out.push(']');
|
||
}
|
||
JsonValue::Obj(entries) => {
|
||
out.push('{');
|
||
for (i, (key, val)) in entries.iter().enumerate() {
|
||
if i > 0 {
|
||
out.push(',');
|
||
}
|
||
write_string(key, out);
|
||
out.push(':');
|
||
write_value(val, out);
|
||
}
|
||
out.push('}');
|
||
}
|
||
}
|
||
}
|
||
|
||
/// 以 JSON 兼容形式渲染数字。整数打印时不带小数部分,符合常见预期
|
||
/// (例如 `3` 而非 `3.0`)。
|
||
/// Render a number in a JSON-compatible form. Integers print without a
|
||
/// fractional part to match common expectations (e.g. `3` not `3.0`).
|
||
fn write_number(n: f64, out: &mut String) {
|
||
if n.is_finite() {
|
||
// 整数走 i64 分支;浮点数走最短表示分支。
|
||
// Integers take the i64 branch; fractional numbers take the shortest-repr branch.
|
||
if n.fract() == 0.0 && n.abs() < 1e16 {
|
||
let _ = write!(out, "{}", n as i64);
|
||
} else {
|
||
// 通过 format! 得到 Ryu 风格的最短表示:可往返且无需引入依赖。
|
||
// Use Ryu-style shortest representation via format!: this gives
|
||
// round-trippable floats without pulling in a dependency.
|
||
let _ = write!(out, "{}", n);
|
||
}
|
||
} else {
|
||
// JSON 无法表示无穷或 NaN;输出 null 以保持有效。
|
||
// JSON has no representation for infinity or NaN. Emit null to stay valid.
|
||
out.push_str("null");
|
||
}
|
||
}
|
||
|
||
/// 将字符串转义并加上引号后写入 `out`。
|
||
/// Escape and quote a string into `out`.
|
||
fn write_string(s: &str, out: &mut String) {
|
||
out.push('"');
|
||
for c in s.chars() {
|
||
match c {
|
||
'"' => out.push_str("\\\""),
|
||
'\\' => out.push_str("\\\\"),
|
||
'\n' => out.push_str("\\n"),
|
||
'\r' => out.push_str("\\r"),
|
||
'\t' => out.push_str("\\t"),
|
||
'\u{0008}' => out.push_str("\\b"),
|
||
'\u{000C}' => out.push_str("\\f"),
|
||
c if (c as u32) < 0x20 => {
|
||
let _ = write!(out, "\\u{:04x}", c as u32);
|
||
}
|
||
c => out.push(c),
|
||
}
|
||
}
|
||
out.push('"');
|
||
}
|