Schema IDL 语法
本文档给出 Fory IDL 的语法与语义参考,覆盖文件结构、类型系统、字段规则、选项与类型注册策略。
编译器使用方式与构建集成请参见 Compiler Guide。 protobuf/FlatBuffers 前端映射请参见 Protocol Buffers IDL Support 与 FlatBuffers IDL Support。
文件结构
一个 Fory IDL 文件通常包含:
- 可选
package声明 - 可选文件级
option - 可选
import语句 - 类型定义(
enum、message、union)
// Optional package declaration
package com.example.models;
// Optional file-level options
option java_package = "com.example.models";
// Import statements
import "common/types.fdl";
// Type definitions
enum Color [id=100] { ... }
message User [id=101] { ... }
message Order [id=102] { ... }
union Event [id=103] { ... }
注释
支持单行注释与块注释:
// This is a single-line comment
/*
* This is a block comment
* that spans multiple lines
*/
message Example {
string name = 1; // Inline comment
}
Package 声明
package 定义文件中所有类型的命名空间。
package com.example.models;
也可以配置 alias(用于自动类型 ID 计算):
package com.example.models alias models_v1;
规则:
- 可选但推荐
- 必须位于任何类型定义之前
- 每个文件最多一个
package - 用于命名空间注册
alias会参与 auto-ID 哈希
语言映射:
| 语言 | package 用法 |
|---|---|
| Java | Java package |
| Python | 模块名(. 转 _) |
| Go | 包名(通常取最后一段) |
| Rust | 模块名(. 转 _) |
| C++ | 命名空间(. 转 ::) |
文件级选项
文件级选项用于控制语言定制代码生成。
语法
option option_name = value;
Java Package 选项
通过 java_package 覆盖 Java 输出包名:
package payment;
option java_package = "com.mycorp.payment.v1";
message Payment {
string id = 1;
}
效果:
- Java 文件输出到
com/mycorp/payment/v1/ - Java
package声明使用该值 - 跨语言类型注册仍以 Fory package(如
payment)为准
Go Package 选项
通过 go_package 指定 Go import path 与包名:
package payment;
option go_package = "github.com/mycorp/apis/gen/payment/v1;paymentv1";
message Payment {
string id = 1;
}
格式:"import/path;package_name" 或仅 "import/path"(包名取最后一段)。
Java Outer Classname 选项
将多个类型包装到一个外层类:
package payment;
option java_outer_classname = "DescriptorProtos";
enum Status {
UNKNOWN = 0;
ACTIVE = 1;
}
message Payment {
string id = 1;
Status status = 2;
}
默认会生成单文件,枚举与消息作为静态内部类型。
Java Multiple Files 选项
控制 Java 是否拆分多文件:
package payment;
option java_outer_classname = "PaymentProtos";
option java_multiple_files = true;
message Payment {
string id = 1;
}
message Receipt {
string id = 1;
}
行为:
java_outer_classname | java_multiple_files | 结果 |
|---|---|---|
| 未设置 | 任意 | 每个类型一个文件 |
| 已设置 | false(默认) | 单文件 + 内部类 |
| 已设置 | true | 强制拆分为多文件 |
多个选项组合
package payment;
option java_package = "com.mycorp.payment.v1";
option go_package = "github.com/mycorp/apis/gen/payment/v1;paymentv1";
option deprecated = true;
Protobuf 扩展语法说明
在 .fdl 中请使用 Fory 原生语法(如 [id=100]、ref、optional、nullable=true)。
(fory).xxx 形式仅用于 .proto(protobuf 前端)。
选项优先级
语言包路径优先级:
- 命令行覆盖(最高)
- 语言选项(
java_package、go_package) - Fory IDL
package(兜底)
跨语言类型注册
默认情况下,注册名由 package + type name(或类型 ID)确定。建议长期保持 package 稳定,以避免跨版本注册不一致。
Import 语句
基本语法
import "common/types.fdl";
多个导入
import "common/types.fdl";
import "domain/user.fdl";
import "domain/order.fdl";
路径解析
导入解析顺序:
- 导入者文件所在目录
- 命令行
-I/--proto_path/--import_path指定目录(按给定顺序)
完整示例
// src/main.fdl
package app;
import "common.fdl";
import "models/user.fdl";
message Main {
common.Meta meta = 1;
models.User user = 2;
}
不支持的 import 写法
- URL 形式(如
https://...) - 绝对路径依赖(不推荐,会破坏可移植性)
import 错误
典型错误:
- 文件不存在
- 搜索路径未包含依赖目录
- 同名文件冲突导致解析到错误版本
Enum 定义
基本语法
enum Status {
UNKNOWN = 0;
ACTIVE = 1;
DISABLED = 2;
}
显式类型 ID
enum Status [id=101] {
UNKNOWN = 0;
ACTIVE = 1;
DISABLED = 2;
}
预留值
enum Status {
UNKNOWN = 0;
ACTIVE = 1;
reserved 2, 3;
reserved 10 to 20;
}
enum 类型选项
常见:id、alias、deprecated。
语言映射
- Java:
enum - Python:
IntEnum - Go:
type + const - Rust:
repr(i32)枚举 - C++:
enum class
枚举前缀处理
针对 protobuf 风格 TYPE_NAME_VALUE,生成器通常会按语言习惯去除冗 余前缀,使 API 更自然。
Message 定义
基本语法
message User {
string name = 1;
int32 age = 2;
}
显式类型 ID
message User [id=100] {
string name = 1;
int32 age = 2;
}
无显式类型 ID
未声明 [id=...] 时,编译器可按配置自动生成类型 ID,或使用 namespace/name 注册。
语言映射
- Java:类 / record(按选项)
- Python:dataclass
- Go:struct
- Rust:struct
- C++:class/struct + 宏元信息
预留字段
message User {
string name = 1;
reserved 2, 3;
reserved 10 to 20;
}
message 类型选项
常见选项:id、alias、evolving、deprecated、namespace、use_record_for_java。
嵌套类型
嵌套 message
message Person {
message PhoneNumber {
string number = 1;
}
PhoneNumber phone = 1;
}
嵌套 enum
message Person {
enum PhoneType {
MOBILE = 0;
HOME = 1;
}
PhoneType type = 1;
}
限定类型名
可使用完整限定名引用嵌套类型,例如 Person.PhoneNumber。
深层嵌套类型
支持多层嵌套,但建议控制层级,避免可读性下降。
各语言生成形态
| 语言 | 嵌套类型形态 |
|---|---|
| Java | Outer.Inner |
| Python | Outer.Inner |
| Rust | outer::Inner |
| C++ | Outer::Inner |
| Go | Outer_Inner(默认) |
嵌套规则
- 嵌套类型名在其父作用域内必须唯一
- 可被同文件后续类型引用
- 可通过 import + 限定名跨文件引用
Union 定义
基本语法
union Animal {
Dog dog = 1;
Cat cat = 2;
}
在 message 中使用 union
message Person {
Animal pet = 1;
}
规则
- case 字段号必须唯一
- case 类型通常为消息类型或可序列化复合类型
- 各语言会生成带 case 判别和访问器的 union 表达
字段定义
基本语法
string name = 1;
带修饰符语法
optional string nickname = 2;
ref Node parent = 3;
list<int32> scores = 4;
字段修饰符
optional
声明字段可为空(null/None):
message User {
optional string email = 1;
}
建议在跨语言场景显式使用,以避免默认值差异。
ref
开启引用跟踪,用于共享对象与循环结构:
message Node {
string name = 1;
ref Node parent = 2;
list<ref Node> children = 3;
}
当运行时全局 ref tracking 开启时,字段级 ref 才会生效。
list
列表字段(repeated 为等价别名):
message Group {
list<string> tags = 1;
}