-
Notifications
You must be signed in to change notification settings - Fork 0
Derive Macros
github-actions[bot] edited this page Aug 13, 2026
·
2 revisions
#[derive(NsonSerialize, NsonDeserialize)] 生成序列化、反序列化与 schema 三份
实现。属性接受 #[njson(...)]、#[nextjson(...)] 与 #[serde(...)] 三种写法
(后者为生态迁移兼容)。本页先看宏到底生成了什么,再列属性清单。
#[derive(NsonSerialize, NsonDeserialize)]
struct Point { x: u64, y: u64 }这三个 derive 各生成一份实现。以下是结构还原(与宏实际输出的模式一致, 简化了无关细节)。
NsonSerialize(序列化)——把字段逐个发射成事件:
impl ::nextjson::NsonSerialize for Point {
fn nextencode<E: ::nextjson::FormatEncoder>(
&self, __e: &mut E,
) -> ::core::result::Result<(), E::Error> {
__e.begin_object()?;
__e.key("x")?;
::nextjson::NsonSerialize::nextencode(&self.x, __e)?;
__e.key("y")?;
::nextjson::NsonSerialize::nextencode(&self.y, __e)?;
__e.end_object()?;
::core::result::Result::Ok(())
}
}注意它没有把 Point 转成 Value 再编码——是直接向 FormatEncoder 发射
事件。这就是"没有中间表示"的字面含义。
NsonDeserialize(反序列化)——字段级槽逐字段解码,__seen 位图跟踪
"哪个字段出现过了":
impl<'de> ::nextjson::NsonDeserialize<'de> for Point {
fn nextdecode_into<__D: ::nextjson::FormatDecoder<'de>>(
__d: &mut __D,
__out: &mut ::nextjson::DecodeSlot<Self>,
) -> ::core::result::Result<(), __D::Error> {
__d.set_expecting(Self::expecting());
let mut __slot0: ::nextjson::private::InitSlot<u64> =
::nextjson::private::InitSlot::new();
let mut __slot1: ::nextjson::private::InitSlot<u64> =
::nextjson::private::InitSlot::new();
let mut __seen: u64 = 0;
__d.begin_object()?;
while let ::core::option::Option::Some(__key) = __d.object_key()? {
match __key.as_ref() {
"x" => { __seen |= 1u64 << 0; __slot0.nextdecode(__d)?; }
"y" => { __seen |= 1u64 << 1; __slot1.nextdecode(__d)?; }
_ => { __d.skip_value()?; }
}
if !__d.object_entry_sep()? { break; }
}
__d.end_object()?;
if !(__seen & (1u64 << 0) != 0) {
return Err(::nextjson::Error::missing_field("x").into());
}
if !(__seen & (1u64 << 1) != 0) {
return Err(::nextjson::Error::missing_field("y").into());
}
// 全部成功,组装并写入调用方的槽
__out.write(Self { x: __slot0.take(), y: __slot1.take() });
::core::result::Result::Ok(())
}
}几个机制细节:
- 字段级槽是
InitSlot(内部就是DecodeSlot,见 Decode Slot)——nextdecode直接把值解码进槽,省掉一层中间Option; -
__seen位图(字段 ≤64 用u64位图,>64 用Vec<bool>)用于必填字段检查: 解码结束时没见过的必填字段 →missing_field报错; - 未知字段默认
skip_value()跳过;deny_unknown_fields时改为unknown_field报错; - 重复字段用
write覆盖旧值,旧值正常 drop; -
set_expecting让类型不匹配错误带上类型名(见 Error Model)。
NsonSchema(schema)——一份编译期常量:
impl ::nextjson::NsonSchema for Point {
const SCHEMA: ::nextjson::TypeSchema =
::nextjson::TypeSchema::Struct(&::nextjson::StructSchema {
name: "Point",
transparent: false,
fields: &[
::nextjson::FieldSchema {
name: "x", orig: "x", required: true, flattened: false,
ty: ::nextjson::TypeSchema::U64,
},
// ... y 同理
],
});
}三个 derive 是从同一份输入(你的类型定义)生成的,所以字段名、重命名、 skip 规则天然一致——这就是"schema 与实现同源"的机制。
宏对
rename_all、skip、flatten、枚举的四种表示(外部/内部/邻接/untagged) 等属性会生成对应的分支代码。属性清单见下。
| 属性 | 作用 |
|---|---|
rename = "..." |
重命名容器(错误消息/内部标识用) |
rename_all = "camelCase" 等 |
字段/变体命名转换;方向性:rename_all(serialize = "...", deserialize = "...") 可分别指定 |
rename_all_fields |
结构体变体的字段命名(+ 方向性) |
tag = "..." |
内部标签枚举 |
content = "..." |
邻接标签枚举 |
untagged |
untagged 枚举(解码走 save/restore 回溯) |
bound = "..." |
替换自动生成的 per-type-param 约束(类型自身 where 子句无条件保留) |
default / default = "path"
|
容器默认实例:缺失字段用默认值补 |
transparent |
透明容器(单字段/单变体) |
deny_unknown_fields |
未知字段报错(与 flatten 组合是编译错误,serde 同款) |
remote = "path" |
为外部类型实现(不碰外部类型定义) |
into / from / try_from
|
用转换类型驱动序列化/反序列化 |
expecting |
解析接受(无 visitor 无行为,文档说明) |
| 属性 | 作用 |
|---|---|
rename = "..." |
字段序列化名 |
default |
缺失时用 Default::default()
|
skip / skip_serializing / skip_deserializing
|
跳过一侧或双侧 |
skip_serializing_if = "path" |
条件跳过(如 Option::is_none) |
flatten |
拍平到父对象(map/struct;元组位置是编译错误) |
serialize_with / deserialize_with / with
|
自定义序列化函数(字段或 newtype 变体) |
getter = "path" |
用 getter 方法取值 |
borrow |
借用字段(生成 'de: 'a where 谓词) |
| 属性 | 作用 |
|---|---|
rename |
变体序列化名 |
rename_all |
该结构体变体的字段命名(不改变体名) |
alias |
反序列化别名(匹配臂加 ` |
other |
兜底 unit 变体(内部/邻接标签;外部/untagged 是编译错误) |
skip_serializing / skip_deserializing
|
单侧跳过 |
serialize_with / deserialize_with / with
|
newtype 变体内含字段的自定义序列化 |
- 普通字段:
<T as NsonSchema>::SCHEMA; -
serialize_with/deserialize_with/with/getter字段:一律TypeSchema::Opaque(外部类型可能没实现NsonSchema); -
PhantomData字段:自动 skip(serde 语义),schema 记Opaque、非 required; -
skip_deserializing(无 path)与容器default:反序列化 impl 给所有类型参数加T: Default(serde 同款)。
容器与变体配置在编译期校验,非法组合直接 compile_error!:
- transparent 枚举/多字段;
-
flatten用在元组位置; -
flatten+skip_serializing_if组合; -
deny_unknown_fields+flatten组合; -
other用在外部/untagged 枚举; - 解析器
parse_input强制消费全部 token(防新语法静默误解析)。
| Bug | 修复 |
|---|---|
pub(crate)/pub(super) 字段解析失败 |
eat_visibility() 匹配 Parenthesis Group |
| 泛型 + where 结构体 E0277 |
bound 只替换自动生成约束,where 无条件保留 |
>64 字段 + skip 时 seen_decl 位图 panic |
改用被跟踪字段序号 |
flatten 声明在中间时拿到全部键 |
分两遍:先显式字段 remove,后 flatten 用剩余键 |
into/from/try_from 泛型 E0107 |
转换 bound 用 Dst<T> 实例化 |
remote + 泛型双泛型参数 |
remote 时 target 用路径原样、不再拼 {ty_generics}
|
变体级 rename_all 改变体名 |
只作用于结构体变体字段 |
嵌套 bound(serialize="..", deserialize="..") 组 |
解析为方向性 Named |
- 零依赖实现细节与坑:Zero-Dependency Macros
- 生成代码背后的契约:Core Contracts / Compile-Time Schema