类型注册
你要序列化的每个 struct 和 enum,在使用前都必须先注册到 Fory 实例中。注册会告诉 Fory:如何在消息中标识该类型,以及如何对其进行编码和解码。
注册 Struct
你可以使用数值 ID 或名称来标识一个 struct。选择一种策略,并在所有共享这类消息的语言中保持一致。
按数值 ID 注册
编码更紧凑。当团队规模较小、可以协调 ID 分配时,这是很好的选择。
const userType = Type.struct(
{ typeId: 1001 },
{
id: Type.int64(),
name: Type.string(),
},
);
const fory = new Fory();
const { serialize, deserialize } = fory.register(userType);
所有读写该类型的运行时都必须使用同一个数值。
按名称注册
更容易跨团队协同,但消息中的元信息会稍大一些。
const userType = Type.struct(
{ typeName: "example.user" },
{
id: Type.int64(),
name: Type.string(),
},
);
const fory = new Fory();
const { serialize, deserialize } = fory.register(userType);
你也可以显式拆分 namespace 和类型名:
const userType = Type.struct(
{ namespace: "example", typeName: "user" },
{
id: Type.int64(),
name: Type.string(),
},
);
同一个类型不要在不同运行时中混用两种策略。 如果一侧使用数值 ID,另一侧使用名称,反序列化会失败。
使用 Decorator 注册
@Type.struct({ typeId: 1001 })
class User {
@Type.int64()
id!: bigint;
@Type.string()
name!: string;
}
const fory = new Fory();
const { serialize, deserialize } = fory.register(User);
当你希望 TypeScript 类声明与 schema 定义放在一起时,基于 decorator 的注册会很方便。
注册 Enum
Fory JavaScript 同时支持普通 JavaScript 风格的枚举对象和 TypeScript enum。
JavaScript 对象枚举
const Color = {
Red: 1,
Green: 2,
Blue: 3,
};
const fory = new Fory();
const colorSerde = fory.register(Type.enum("example.color", Color));
TypeScript enum
enum Status {
Pending = "pending",
Active = "active",
}
const fory = new Fory();
fory.register(Type.enum("example.status", Status));
注册作用域
注册是以 Fory 实例为作用域的。如果你创建了两个实例,就需要在两个实例中都注册 schema。
register 的返回值
fory.register(schema) 会返回一个绑定后的序列化器对:
const { serialize, deserialize } = fory.register(orderType);
// serialize returns Uint8Array bytes
const bytes = serialize({ id: 1n, total: 99.99 });
// deserialize returns the decoded value
const order = deserialize(bytes);
把这个返回对保存起来并重复复用,它就是性能最优的调用路径。
字段选项
可空字段
如果字段可能为 null,请显式标记。向不可空字段传入 null 会抛出异常。
Type.string().setNullable(true);
字段上的引用跟踪
当同一个对象实例可能出现在多个字段中时,需要启用字段级引用跟踪,详见 引用:
Type.struct("example.node").setTrackingRef(true);
只有在同时设置了 new Fory({ ref: true }) 时,这个选项才会生效。
多态字段
当字段在运行时可能承载不同类型的值时,可以使用 Type.any():
const eventType = Type.struct("example.event", {
kind: Type.string(),
payload: Type.any(),
});
如果你需要更细粒度地控制某个 struct 字段如何处理运行时类型,可以调用 .setDynamic(Dynamic.FALSE),表示始终按声明类型处理;或者调用 .setDynamic(Dynamic.TRUE),表示始终写入运行时类型。默认值 Dynamic.AUTO 适用于绝大多数场景。
如何选择 ID 与名称
以下情况适合使用数值 ID:
- 你希望消息尽可能小
- 你的组织能够保证 ID 稳定且全局唯一
- 服务之间协同非常紧密
以下情况适合使用名称:
- 不同团队独立定义类型
- schema 本身已经通过 package/module name 标识
- 可以接受稍大的元信息开销
跨语言
如果要让消息在 JavaScript 与其他运行时之间往返,双方必须对某个类型使用相同的类型标识:相同的数值 ID,或相同的 namespace + typeName。参见 跨语言。