TL;DR:如果你已经有 .proto 或 .fbs Schema,并且希望支持共享/循环引用,那么使用 Fory 编译器时你可以保留这些 Schema,只需添加一小组 Fory 选项,再通过 foryc 生成 Fory 支持语言中的原生习惯模型,随后即可序列化对象图,无需手工通过 *_id 重建关联,也无需把整份 Schema 重写为 Fory Schema。
- GitHub: https://github.com/apache/fory
- 编译器文档: https://fory.apache.org/docs/compiler
- 安装:
pip install fory-compiler
Protobuf 与 FlatBuffers Schema 的缺口
很多生产模型本质上是对象图,而不是纯树结构:
- 父指针(
child.parent) - 共享节点(两条边指向同一个对象)
- 循环(A -> B -> A)
在原生 protobuf 或 FlatBuffers Schema 设计中,这类关系通常只能通过手工 ID 链接(parent_id、child_ids)间接表示,并在解码后由应用代码重建对象关系。
Apache Fory 增加了 Schema 级引用跟踪能力,可以通过 Fory 选项在 protobuf/FlatBuffers 源文件中直接声明这类对象图语义。
保留 .proto 与 .fbs,补充对象图语义
你不需要先把所有内容重写为 .fdl。
- 对 protobuf 输入,使用
(fory).ref、(fory).weak_ref等选项。 - 对 FlatBuffers 输入,使用
fory_ref:true、fory_weak_ref:true等属性。 - 使用
foryc编译。 - 生成 Fory 模型并使用 Fory 编码格式。
重要说明:这个路径复用了 Schema 的语法与结构,但序列化输出是 Fory 二进制协议,不与 protobuf/FlatBuffers 编码格式兼容。
Protobuf:从 ID 变通到 ref
Protobuf 风格变通(面向值树)
message TreeNode {
string id = 1;
string parent_id = 2;
repeated string child_ids = 3;
}
Protobuf + Fory 选项(对象图语义)
syntax = "proto3";
message TreeNode {
TreeNode parent = 1 [(fory).weak_ref = true];
repeated TreeNode children = 2 [(fory).ref = true];
}
含义如下:
weak_ref = true隐含启用引用跟踪,并在相关语言中生成弱指针语义(例如 C++/Rust)。- 对
repeated字段,(fory).ref = true作用于列表元素。 - 对
map<K, V>,(fory).ref = true作用于 map 的 value。
你还可以调节 Rust 指针风格:
message Graph {
Node root = 1 [(fory).ref = true, (fory).thread_safe_pointer = false];
}
FlatBuffers:添加 fory_ref 属性
带 Fory 引用属性的 FlatBuffers Schema
namespace demo;
table Node {
parent: Node (fory_weak_ref: true);
children: [Node] (fory_ref: true);
cached: Node (fory_ref: true, fory_thread_safe_pointer: false);
}
语义:
fory_weak_ref:true隐含启用引用跟踪。fory_thread_safe_pointer会影响启用引用跟踪字段的指针风格。- 对列表字段,
fory_ref:true作用于元素。
FlatBuffers 输入会先被翻译为 Fory IR,再走标准 Fory 代码生成流程。生成后的 API 是原生对象模型(不是 FlatBuffers ByteBuffer 包装器 API)。
在提交前先检查翻译结果
你可以先检查 .proto 或 .fbs 被翻译后的结果:
# Protobuf to translated Fory schema
foryc schema.proto --emit-fdl
# FlatBuffers to translated Fory schema
foryc schema.fbs --emit-fdl --emit-fdl-path ./translated
然后生成代码:
foryc schema.proto \
--java_out=./java/gen \
--python_out=./python/gen \
--go_out=./go/gen \
--rust_out=./rust/gen \
--cpp_out=./cpp/gen
这是一个对迁移友好的闭环:
- 保留已有 Schema 文件。
- 只在需要对象图语义的字段上添加 Fory 引用选项。
- 检查翻译后的
.fdl。 - 生成代码并执行 roundtrip 测试。
代码生成后你能得到什么
使用 Fory 代码生成后,你会得到原生语言模型和具备引用感知能力的序列化行为:
- Java:带 Fory 元数据的 POJO/record
- Python:dataclass + 注册辅助方法
- Go:带 tag 的 struct
- Rust:带引用指针映射的原生 struct
- C++:带 shared/weak pointer 映射的原生 class/struct
- C#、Swift 等:符合语言习惯的生成模型
对于对象图密集的业务流程,这可以移除常见的手工 ID 链接回填逻辑。
需要注意的行为边界
-
这不是 protobuf 或 FlatBuffers 编码格式兼容。 它复用了 Schema 前端,但线上字节是 Fory 协议。
-
Protobuf unknown-field 行为不同。 Protobuf 会保留 unknown fields;Fory 不会保留 protobuf unknown-field 载荷。
-
引用跟踪应当显式且最小化使用。
ref/weak_ref应用于真正共享或成环的对象图部分,而不是所有字段都开启。
实际迁移模式
如果你已经在生产环境运行 protobuf 或 FlatBuffers:
- 保持 package/namespace 名称稳定。
- 只在图结构字段上添加 Fory 引用选项。
- 用
foryc生成代码并运行 roundtrip 测试。 - 先迁移内部对象密集路径。
- 在需要时保持外部 protobuf/gRPC 边界不变。
这样你就可以渐进式引入对象图语义,而不用一次性重写全部 Schema。
总结
Protobuf 和 FlatBuffers Schema 文件可以成为 Fory 对象图感知序列化的起点,而不是阻碍。
通过补充一组小而明确的 Fory 扩展选项(ref、weak_ref、指针风格提示),你可以在已有 .proto/.fbs 源文件中直接表达共享身份和循环链接,生成原生模型,并移除对象图密集系统中手工重建对象关系的代码。
如果你的数据模型本质是图,就应在 Schema 里把图语义显式表达出来。
快速开始:
pip install fory-compiler
foryc --help
参考资料:
- 对象图 Schema IDL 参考文章: https://fory.apache.org/blog/fory_schema_idl_for_object_graph
- Protobuf IDL 支持: https://fory.apache.org/docs/compiler/protobuf_idl_support
- FlatBuffers IDL 支持: https://fory.apache.org/docs/compiler/flatbuffers_idl
- Schema 语法(
ref、optional、union): https://fory.apache.org/docs/compiler/syntax - 代码生成总览: https://fory.apache.org/docs/compiler/generated_code
