Skip to content

Cross Format Relay

github-actions[bot] edited this page Aug 13, 2026 · 2 revisions

Cross-Format Relay

cross_format::EventSink 是仓库自有的格式中立事件协议,让 JSON 与 CBOR 流式互转,不构造中间 Value 树,内存不随文档树增长。本页用一个具体转换 把机制走一遍。

它解决什么问题

常规的多格式引擎是"类型驱动"的:encode_with(&value, formats::Json) 需要 value 有类型。但有些场景你只想把字节转成字节——代理、日志、网关收到 JSON 要转发成 CBOR,中间谁都不想解析语义。硬要建一棵 Value 树再编码,大文档 的内存开销就是白付的。

EventSink 是"数据驱动"的答案:不需要类型、不建树,逐事件从源格式流到 目标格式。

EventSink 契约

pub trait EventSink {
    fn null(&mut self) -> Result<()>;
    fn boolean(&mut self, value: bool) -> Result<()>;
    fn number(&mut self, value: Number) -> Result<()>;
    fn string(&mut self, value: &str) -> Result<()>;
    fn begin_array(&mut self) -> Result<()>;
    fn end_array(&mut self) -> Result<()>;
    fn begin_object(&mut self) -> Result<()>;
    fn object_key(&mut self, key: &str) -> Result<()>;
    fn end_object(&mut self) -> Result<()>;
}

设计巧思object_keystring 分离——防止目标格式意外接受 JSON 无法 表示的非字符串键。实现必须对非法事件顺序返回错误。

一个具体例子:JSON → CBOR

let json = br#"{"name":"NextJson","values":[1,2,3]}"#;
let cbor = cross_format::json_to_cbor(json)?;

内部发生的事(json_into 逐事件喂给 CborSink):

json_into 读到        喂给 CborSink 的事件        CborSink 写出的 CBOR 字节
--------------------------------------------------------------
{                      begin_object               0xA2        (map, 2 对)
"name"                 object_key("name")         0x64 'n' 'a' 'm' 'e'
"NextJson"             string("NextJson")         0x69 "NextJson"
"values"               object_key("values")       0x66 'v' 'a' 'l' 'u' 'e' 's'
[                      begin_array                0x83        (array, 3 个)
1                      number(1)                  0x01
2                      number(2)                  0x02
3                      number(3)                  0x03
]                      end_array                  (无额外字节)
}                      end_object                 (无额外字节)

整个过程不出现任何中间 ValueCborSink 内部维护一个小状态机("现在在对象里 还是数组里、下一个该写键还是写值"),begin_object 时先数后续有多少个 object_key 才知道 map 长度前缀——所以 CborSink 也依赖"每个条目调用一次 object_key"这个契约。

内置 Sink

  • JsonSink:事件 → 紧凑/美化 JSON;
  • CborSink:事件 → RFC 8949 JSON 兼容 profile CBOR。

入口 API

函数 作用
json_into(input, sink) JSON 输入流式喂给任意 EventSink
json_into_with_config(input, config, sink) 带嵌套配置
cbor_into(input, sink) / cbor_into_with_max_depth CBOR 输入流式喂给 sink
json_to_cbor / json_to_cbor_writer JSON → CBOR 字节/写出
cbor_to_json / cbor_to_json_writer / cbor_to_json_pretty / cbor_to_json_with_config CBOR → JSON
use nextjson::cross_format;

let json = br#"{"name":"NextJson","values":[1,2,3]}"#;
let cbor = cross_format::json_to_cbor(json)?;
let json_again = cross_format::cbor_to_json(&cbor)?;
assert_eq!(json_again, json);

零拷贝边界

  • 未转义 JSON 字符串在 json_into 里以输入切片的直接借用传给 sink;
  • 转义字符串由 JSON 反转义必然物化——这是格式语义要求,文档不伪称零分配。

为什么值得单独一个模块

  • 常规多格式引擎(Multi-Format Engine)是"类型驱动":需要 NsonSerialize / NsonDeserialize 目标类型;
  • EventSink 是"数据驱动":只要目标格式能表示 JSON 数据模型,就能互转, 不需要类型、不建树。适合代理、日志、网关等"只转发不解析语义"的场景。

诚实边界

  • CBOR 侧只接受 JSON 兼容 profile:原始 byte string、非字符串 map key、 非有限浮点、未知语义 tag 明确报错——防止 CBOR→JSON 时静默语义损失;
  • 每条路径都有深度上限(默认 128)。

Clone this wiki locally