跳到主要内容
版本:0.15

生成代码

本文档说明各目标语言的代码生成结果与结构。

Fory IDL 生成的类型遵循宿主语言习惯,可直接作为领域对象使用。生成代码同时包含 to/from bytes 辅助方法和类型注册辅助逻辑。

参考 Schema

下面示例基于两个真实 schema:

  1. addressbook.fdl(显式类型 ID)
  2. auto_id.fdl(未显式声明类型 ID)

addressbook.fdl(节选)

package addressbook;

option go_package = "github.com/myorg/myrepo/gen/addressbook;addressbook";

message Person [id=100] {
string name = 1;
int32 id = 2;

enum PhoneType [id=101] {
PHONE_TYPE_MOBILE = 0;
PHONE_TYPE_HOME = 1;
PHONE_TYPE_WORK = 2;
}

message PhoneNumber [id=102] {
string number = 1;
PhoneType phone_type = 2;
}

list<PhoneNumber> phones = 7;
Animal pet = 8;
}

message Dog [id=104] {
string name = 1;
int32 bark_volume = 2;
}

message Cat [id=105] {
string name = 1;
int32 lives = 2;
}

union Animal [id=106] {
Dog dog = 1;
Cat cat = 2;
}

message AddressBook [id=103] {
list<Person> people = 1;
map<string, Person> people_by_name = 2;
}

auto_id.fdl(节选)

package auto_id;

enum Status {
UNKNOWN = 0;
OK = 1;
}

message Envelope {
string id = 1;

message Payload {
int32 value = 1;
}

union Detail {
Payload payload = 1;
string note = 2;
}

Payload payload = 2;
Detail detail = 3;
Status status = 4;
}

union Wrapper {
Envelope envelope = 1;
string raw = 2;
}

Java

输出布局

对于 package addressbook,Java 输出通常位于:

  • <java_out>/addressbook/
  • 类型文件:AddressBook.javaPerson.javaDog.javaCat.javaAnimal.java
  • 注册辅助类:AddressbookForyRegistration.java

类型生成

message 会生成带 @ForyField、默认构造器、getter/setter 以及字节辅助方法的 Java 类:

public class Person {
public static enum PhoneType {
MOBILE,
HOME,
WORK;
}

public static class PhoneNumber {
@ForyField(id = 1)
private String number;

@ForyField(id = 2)
private PhoneType phoneType;

public byte[] toBytes() { ... }
public static PhoneNumber fromBytes(byte[] bytes) { ... }
}

@ForyField(id = 1)
private String name;

@ForyField(id = 8)
private Animal pet;

public byte[] toBytes() { ... }
public static Person fromBytes(byte[] bytes) { ... }
}

union 会生成继承 org.apache.fory.type.union.Union 的类:

public final class Animal extends Union {
public enum AnimalCase {
DOG(1),
CAT(2);
public final int id;
AnimalCase(int id) { this.id = id; }
}

public static Animal ofDog(Dog v) { ... }
public AnimalCase getAnimalCase() { ... }
public int getAnimalCaseId() { ... }

public boolean hasDog() { ... }
public Dog getDog() { ... }
public void setDog(Dog v) { ... }
}

注册

生成的注册辅助方法:

public static void register(Fory fory) {
org.apache.fory.resolver.TypeResolver resolver = fory.getTypeResolver();
resolver.registerUnion(Animal.class, 106L, new org.apache.fory.serializer.UnionSerializer(fory, Animal.class));
resolver.register(Person.class, 100L);
resolver.register(Person.PhoneType.class, 101L);
resolver.register(Person.PhoneNumber.class, 102L);
resolver.register(Dog.class, 104L);
resolver.register(Cat.class, 105L);
resolver.register(AddressBook.class, 103L);
}

对于未显式 [id=...] 的 schema,注册代码会使用计算得到的数值 ID(例如 auto_id.fdl):

resolver.register(Status.class, 1124725126L);
resolver.registerUnion(Wrapper.class, 1471345060L, new org.apache.fory.serializer.UnionSerializer(fory, Wrapper.class));
resolver.register(Envelope.class, 3022445236L);
resolver.registerUnion(Envelope.Detail.class, 1609214087L, new org.apache.fory.serializer.UnionSerializer(fory, Envelope.Detail.class));
resolver.register(Envelope.Payload.class, 2862577837L);

若设置 option enable_auto_type_id = false;,则按命名空间与类型名注册:

resolver.register(Config.class, "myapp.models", "Config");
resolver.registerUnion(
Holder.class,
"myapp.models",
"Holder",
new org.apache.fory.serializer.UnionSerializer(fory, Holder.class));

使用示例

Person person = new Person();
person.setName("Alice");
person.setPet(Animal.ofDog(new Dog()));

byte[] data = person.toBytes();
Person restored = Person.fromBytes(data);

Python

输出布局

Python 每个 schema 文件生成一个模块,例如:

  • <python_out>/addressbook.py

类型生成

union 生成 case 枚举与 Union 子类,并提供类型化辅助方法:

class AnimalCase(Enum):
DOG = 1
CAT = 2

class Animal(Union):
@classmethod
def dog(cls, v: Dog) -> "Animal": ...

def case(self) -> AnimalCase: ...
def case_id(self) -> int: ...

def is_dog(self) -> bool: ...
def dog_value(self) -> Dog: ...
def set_dog(self, v: Dog) -> None: ...

message 生成 @pyfory.dataclass 类型,嵌套类型保持嵌套:

@pyfory.dataclass
class Person:
class PhoneType(IntEnum):
MOBILE = 0
HOME = 1
WORK = 2

@pyfory.dataclass
class PhoneNumber:
number: str = pyfory.field(id=1, default="")
phone_type: Person.PhoneType = pyfory.field(id=2, default=None)

name: str = pyfory.field(id=1, default="")
phones: List[Person.PhoneNumber] = pyfory.field(id=7, default_factory=list)
pet: Animal = pyfory.field(id=8, default=None)

def to_bytes(self) -> bytes: ...
@classmethod
def from_bytes(cls, data: bytes) -> "Person": ...

注册

生成注册函数:

def register_addressbook_types(fory: pyfory.Fory):
fory.register_union(Animal, type_id=106, serializer=AnimalSerializer(fory))
fory.register_type(Person, type_id=100)
fory.register_type(Person.PhoneType, type_id=101)
fory.register_type(Person.PhoneNumber, type_id=102)
fory.register_type(Dog, type_id=104)
fory.register_type(Cat, type_id=105)
fory.register_type(AddressBook, type_id=103)

未显式 [id=...] 时,注册代码使用计算得到的数值 ID:

fory.register_type(Status, type_id=1124725126)
fory.register_union(Wrapper, type_id=1471345060, serializer=WrapperSerializer(fory))
fory.register_type(Envelope, type_id=3022445236)
fory.register_union(Envelope.Detail, type_id=1609214087, serializer=Envelope.DetailSerializer(fory))
fory.register_type(Envelope.Payload, type_id=2862577837)

若设置 option enable_auto_type_id = false;

fory.register_type(Config, namespace="myapp.models", typename="Config")
fory.register_union(
Holder,
namespace="myapp.models",
typename="Holder",
serializer=HolderSerializer(fory),
)

使用示例

person = Person(name="Alice", pet=Animal.dog(Dog(name="Rex", bark_volume=10)))

data = person.to_bytes()
restored = Person.from_bytes(data)

Rust

输出布局

Rust 每个 schema 生成一个模块文件,例如:

  • <rust_out>/addressbook.rs

类型生成

union 映射为 Rust enum,并用 #[fory(id = ...)] 声明 case ID:

#[derive(ForyObject, Debug, Clone, PartialEq)]
pub enum Animal {
#[fory(id = 1)]
Dog(Dog),
#[fory(id = 2)]
Cat(Cat),
}

嵌套类型生成嵌套 module:

pub mod person {
#[derive(ForyObject, Debug, Clone, PartialEq, Default)]
#[repr(i32)]
pub enum PhoneType {
#[default]
Mobile = 0,
Home = 1,
Work = 2,
}

#[derive(ForyObject, Debug, Clone, PartialEq, Default)]
pub struct PhoneNumber {
#[fory(id = 1)]
pub number: String,
#[fory(id = 2)]
pub phone_type: PhoneType,
}
}

message 会 derive(ForyObject) 并生成 to_bytes/from_bytes

#[derive(ForyObject, Debug, Clone, PartialEq, Default)]
pub struct Person {
#[fory(id = 1)]
pub name: String,
#[fory(id = 7)]
pub phones: Vec<person::PhoneNumber>,
#[fory(id = 8, type_id = "union")]
pub pet: Animal,
}

注册

生成注册函数:

pub fn register_types(fory: &mut Fory) -> Result<(), fory::Error> {
fory.register_union::<Animal>(106)?;
fory.register::<person::PhoneType>(101)?;
fory.register::<person::PhoneNumber>(102)?;
fory.register::<Person>(100)?;
fory.register::<Dog>(104)?;
fory.register::<Cat>(105)?;
fory.register::<AddressBook>(103)?;
Ok(())
}

未显式 [id=...] 时,注册代码使用计算得到的数值 ID:

fory.register::<Status>(1124725126)?;
fory.register_union::<Wrapper>(1471345060)?;
fory.register::<Envelope>(3022445236)?;
fory.register_union::<envelope::Detail>(1609214087)?;
fory.register::<envelope::Payload>(2862577837)?;

若设置 option enable_auto_type_id = false;

fory.register_by_namespace::<Config>("myapp.models", "Config")?;
fory.register_union_by_namespace::<Holder>("myapp.models", "Holder")?;

使用示例

let person = Person {
name: "Alice".into(),
pet: Animal::Dog(Dog::default()),
..Default::default()
};

let bytes = person.to_bytes()?;
let restored = Person::from_bytes(&bytes)?;

C++

输出布局

C++ 每个 schema 文件生成一个头文件,例如:

  • <cpp_out>/addressbook.h

类型生成

message 会生成 final 类,并包含类型化访问器与字节辅助方法:

class Person final {
public:
class PhoneNumber final {
public:
const std::string& number() const;
std::string* mutable_number();
template <class Arg, class... Args>
void set_number(Arg&& arg, Args&&... args);

fory::Result<std::vector<uint8_t>, fory::Error> to_bytes() const;
static fory::Result<PhoneNumber, fory::Error> from_bytes(const std::vector<uint8_t>& data);
};

const std::string& name() const;
std::string* mutable_name();
template <class Arg, class... Args>
void set_name(Arg&& arg, Args&&... args);

const Animal& pet() const;
Animal* mutable_pet();
};

可选 message 字段会生成 has_xxxmutable_xxxclear_xxx API:

class Envelope final {
public:
bool has_payload() const { return payload_ != nullptr; }
const Envelope::Payload& payload() const { return *payload_; }
Envelope::Payload* mutable_payload() {
if (!payload_) {
payload_ = std::make_unique<Envelope::Payload>();
}
return payload_.get();
}
void clear_payload() { payload_.reset(); }

private:
std::unique_ptr<Envelope::Payload> payload_;
};

union 会生成基于 std::variant 的封装:

class Animal final {
public:
enum class AnimalCase : uint32_t {
DOG = 1,
CAT = 2,
};

static Animal dog(Dog v);
static Animal cat(Cat v);

AnimalCase animal_case() const noexcept;
uint32_t animal_case_id() const noexcept;

bool is_dog() const noexcept;
const Dog* as_dog() const noexcept;
Dog* as_dog() noexcept;
const Dog& dog() const;
Dog& dog();

template <class Visitor>
decltype(auto) visit(Visitor&& vis) const;

private:
std::variant<Dog, Cat> value_;
};

生成头文件还会包含 FORY_UNIONFORY_FIELD_CONFIGFORY_ENUMFORY_STRUCT 等序列化元信息宏。

注册

生成注册函数:

inline void register_types(fory::serialization::BaseFory& fory) {
fory.register_union<Animal>(106);
fory.register_enum<Person::PhoneType>(101);
fory.register_struct<Person::PhoneNumber>(102);
fory.register_struct<Person>(100);
fory.register_struct<Dog>(104);
fory.register_struct<Cat>(105);
fory.register_struct<AddressBook>(103);
}

未显式 [id=...] 时,注册代码使用计算得到的数值 ID:

fory.register_enum<Status>(1124725126);
fory.register_union<Wrapper>(1471345060);
fory.register_struct<Envelope>(3022445236);
fory.register_union<Envelope::Detail>(1609214087);
fory.register_struct<Envelope::Payload>(2862577837);

若设置 option enable_auto_type_id = false;

fory.register_struct<Config>("myapp.models", "Config");
fory.register_union<Holder>("myapp.models", "Holder");

使用示例

addressbook::Person person;
person.set_name("Alice");
*person.mutable_pet() = addressbook::Animal::dog(addressbook::Dog{});

auto bytes = person.to_bytes();
auto restored = addressbook::Person::from_bytes(bytes.value());

Go

输出布局

Go 输出路径受 schema 选项与 --go_out 共同影响。

对于 addressbook.fdl,若配置了 go_package,生成结果会遵循对应 import path/package(位于 --go_out 根目录下)。

若未配置 go_package,则使用 --go_out 指定目录,并按 package 规则生成文件名与包名。

类型生成

嵌套类型默认使用下划线命名(Person_PhoneTypePerson_PhoneNumber):

type Person_PhoneType int32

const (
Person_PhoneTypeMobile Person_PhoneType = 0
Person_PhoneTypeHome Person_PhoneType = 1
Person_PhoneTypeWork Person_PhoneType = 2
)

type Person_PhoneNumber struct {
Number string `fory:"id=1"`
PhoneType Person_PhoneType `fory:"id=2"`
}

message 会生成带 fory tag 的 struct 与字节辅助方法:

type Person struct {
Name string `fory:"id=1"`
Id int32 `fory:"id=2,compress=true"`
Phones []Person_PhoneNumber `fory:"id=7"`
Pet Animal `fory:"id=8"`
}

func (m *Person) ToBytes() ([]byte, error) { ... }
func (m *Person) FromBytes(data []byte) error { ... }

union 会生成 case struct,并提供构造器/访问器/visitor API:

type AnimalCase uint32

type Animal struct {
case_ AnimalCase
value any
}

func DogAnimal(v *Dog) Animal { ... }
func CatAnimal(v *Cat) Animal { ... }

func (u Animal) Case() AnimalCase { ... }
func (u Animal) AsDog() (*Dog, bool) { ... }
func (u Animal) Visit(visitor AnimalVisitor) error { ... }

注册

生成注册函数:

func RegisterTypes(f *fory.Fory) error {
if err := f.RegisterUnion(Animal{}, 106, fory.NewUnionSerializer(...)); err != nil {
return err
}
if err := f.RegisterEnum(Person_PhoneType(0), 101); err != nil {
return err
}
if err := f.RegisterStruct(Person_PhoneNumber{}, 102); err != nil {
return err
}
if err := f.RegisterStruct(Person{}, 100); err != nil {
return err
}
return nil
}

未显式 [id=...] 时,注册代码使用计算得到的数值 ID:

if err := f.RegisterEnum(Status(0), 1124725126); err != nil { ... }
if err := f.RegisterUnion(Wrapper{}, 1471345060, fory.NewUnionSerializer(...)); err != nil { ... }
if err := f.RegisterStruct(Envelope{}, 3022445236); err != nil { ... }
if err := f.RegisterUnion(Envelope_Detail{}, 1609214087, fory.NewUnionSerializer(...)); err != nil { ... }
if err := f.RegisterStruct(Envelope_Payload{}, 2862577837); err != nil { ... }

若设置 option enable_auto_type_id = false;

if err := f.RegisterNamedStruct(Config{}, "myapp.models.Config"); err != nil { ... }
if err := f.RegisterNamedUnion(Holder{}, "myapp.models.Holder", fory.NewUnionSerializer(...)); err != nil { ... }

go_nested_type_style 可控制嵌套类型命名风格:

option go_nested_type_style = "camelcase";

使用示例

person := &Person{
Name: "Alice",
Pet: DogAnimal(&Dog{Name: "Rex"}),
}

data, err := person.ToBytes()
if err != nil {
panic(err)
}
var restored Person
if err := restored.FromBytes(data); err != nil {
panic(err)
}

跨语言说明

类型 ID 行为

  • 显式 [id=...] 会直接用于生成注册代码。
  • 未声明类型 ID 时,生成代码会使用计算得到的数值 ID(参见 auto_id.* 输出)。
  • 若设置 option enable_auto_type_id = false;,则生成代码改为使用 namespace/type-name 注册 API,而非数值 ID。

嵌套类型形态

语言嵌套类型形式
JavaPerson.PhoneNumber
PythonPerson.PhoneNumber
Rustperson::PhoneNumber
C++Person::PhoneNumber
GoPerson_PhoneNumber(默认)

字节辅助方法命名

语言辅助方法名称
JavatoBytes / fromBytes
Pythonto_bytes / from_bytes
Rustto_bytes / from_bytes
C++to_bytes / from_bytes
GoToBytes / FromBytes