blob: dcf9bce3b1bf1bdee94a7b88f5e17442b0b2b6df [file] [view]
---
title: Generated Code
sidebar_position: 5
id: generated_code
license: |
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
---
This document explains generated code for each target language.
Fory IDL generated types are idiomatic in host languages and can be used directly as domain objects. Generated types also include `to/from bytes` helpers and registration helpers.
## Reference Schemas
The examples below use two real schemas:
1. `addressbook.fdl` (explicit type IDs)
2. `auto_id.fdl` (no explicit type IDs)
### `addressbook.fdl` (excerpt)
```protobuf
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` (excerpt)
```protobuf
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
### Output Layout
For `package addressbook`, Java output is generated under:
- `<java_out>/addressbook/`
- Type files: `AddressBook.java`, `Person.java`, `Dog.java`, `Cat.java`, `Animal.java`
- Registration helper: `AddressbookForyRegistration.java`
### Type Generation
Messages generate Java classes with `@ForyField`, default constructors, getters/setters, and byte helpers:
```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) { ... }
}
```
Unions generate classes extending `org.apache.fory.type.union.Union`:
```java
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) { ... }
}
```
### Registration
Generated registration helper:
```java
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);
}
```
For schemas without explicit `[id=...]`, generated registration uses computed numeric IDs (for example from `auto_id.fdl`):
```java
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);
```
If `option enable_auto_type_id = false;` is set, registration uses namespace and type name:
```java
resolver.register(Config.class, "myapp.models", "Config");
resolver.registerUnion(
Holder.class,
"myapp.models",
"Holder",
new org.apache.fory.serializer.UnionSerializer(fory, Holder.class));
```
### Usage
```java
Person person = new Person();
person.setName("Alice");
person.setPet(Animal.ofDog(new Dog()));
byte[] data = person.toBytes();
Person restored = Person.fromBytes(data);
```
## Python
### Output Layout
Python output is one module per schema file, for example:
- `<python_out>/addressbook.py`
### Type Generation
Unions generate a case enum plus a `Union` subclass with typed helpers:
```python
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: ...
```
Messages generate `@pyfory.dataclass` types, and nested types stay nested:
```python
@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": ...
```
### Registration
Generated registration function:
```python
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)
```
For schemas without explicit `[id=...]`, generated registration uses computed numeric IDs:
```python
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)
```
If `option enable_auto_type_id = false;` is set:
```python
fory.register_type(Config, namespace="myapp.models", typename="Config")
fory.register_union(
Holder,
namespace="myapp.models",
typename="Holder",
serializer=HolderSerializer(fory),
)
```
### Usage
```python
person = Person(name="Alice", pet=Animal.dog(Dog(name="Rex", bark_volume=10)))
data = person.to_bytes()
restored = Person.from_bytes(data)
```
## Rust
### Output Layout
Rust output is one module file per schema, for example:
- `<rust_out>/addressbook.rs`
### Type Generation
Unions map to Rust enums with `#[fory(id = ...)]` case attributes:
```rust
#[derive(ForyObject, Debug, Clone, PartialEq)]
pub enum Animal {
#[fory(id = 1)]
Dog(Dog),
#[fory(id = 2)]
Cat(Cat),
}
```
Nested types generate nested modules:
```rust
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,
}
}
```
Messages derive `ForyObject` and include `to_bytes`/`from_bytes` helpers:
```rust
#[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,
}
```
### Registration
Generated registration function:
```rust
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(())
}
```
For schemas without explicit `[id=...]`, generated registration uses computed numeric IDs:
```rust
fory.register::<Status>(1124725126)?;
fory.register_union::<Wrapper>(1471345060)?;
fory.register::<Envelope>(3022445236)?;
fory.register_union::<envelope::Detail>(1609214087)?;
fory.register::<envelope::Payload>(2862577837)?;
```
If `option enable_auto_type_id = false;` is set:
```rust
fory.register_by_namespace::<Config>("myapp.models", "Config")?;
fory.register_union_by_namespace::<Holder>("myapp.models", "Holder")?;
```
### Usage
```rust
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++
### Output Layout
C++ output is one header per schema file, for example:
- `<cpp_out>/addressbook.h`
### Type Generation
Messages generate `final` classes with typed accessors and byte helpers:
```cpp
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();
};
```
Optional message fields generate `has_xxx`, `mutable_xxx`, and `clear_xxx` APIs:
```cpp
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_;
};
```
Unions generate `std::variant` wrappers:
```cpp
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_;
};
```
Generated headers also include `FORY_UNION`, `FORY_FIELD_CONFIG`, `FORY_ENUM`, and `FORY_STRUCT` macros for serialization metadata.
### Registration
Generated registration function:
```cpp
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);
}
```
For schemas without explicit `[id=...]`, generated registration uses computed numeric IDs:
```cpp
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);
```
If `option enable_auto_type_id = false;` is set:
```cpp
fory.register_struct<Config>("myapp.models", "Config");
fory.register_union<Holder>("myapp.models", "Holder");
```
### Usage
```cpp
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
### Output Layout
Go output path depends on schema options and `--go_out`.
For `addressbook.fdl`, `go_package` is configured and generated output follows the configured import path/package (for example under your `--go_out` root).
Without `go_package`, output uses the requested `--go_out` directory and package-derived file naming.
### Type Generation
Nested types use underscore naming by default (`Person_PhoneType`, `Person_PhoneNumber`):
```go
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"`
}
```
Messages generate structs with `fory` tags and byte helpers:
```go
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 { ... }
```
Unions generate typed case structs with constructors/accessors/visitor APIs:
```go
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 { ... }
```
### Registration
Generated registration function:
```go
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
}
```
For schemas without explicit `[id=...]`, generated registration uses computed numeric IDs:
```go
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 { ... }
```
If `option enable_auto_type_id = false;` is set:
```go
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` controls nested type naming:
```protobuf
option go_nested_type_style = "camelcase";
```
The CLI flag `--go_nested_type_style` overrides this schema option when both are set.
### Usage
```go
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)
}
```
## C\#
### Output Layout
C# output is one `.cs` file per schema, for example:
- `<csharp_out>/addressbook/addressbook.cs`
### Type Generation
Messages generate `[ForyObject]` classes with C# properties and byte helpers:
```csharp
[ForyObject]
public sealed partial class Person
{
public string Name { get; set; } = string.Empty;
public int Id { get; set; }
public List<Person.PhoneNumber> Phones { get; set; } = new();
public Animal Pet { get; set; } = null!;
public byte[] ToBytes() { ... }
public static Person FromBytes(byte[] data) { ... }
}
```
Unions generate `Union` subclasses with typed case helpers:
```csharp
public sealed class Animal : Union
{
public static Animal Dog(Dog value) { ... }
public static Animal Cat(Cat value) { ... }
public bool IsDog => ...;
public Dog DogValue() { ... }
}
```
### Registration
Each schema generates a registration helper:
```csharp
public static class AddressbookForyRegistration
{
public static void Register(Fory fory)
{
fory.Register<addressbook.Animal>((uint)106);
fory.Register<addressbook.Person>((uint)100);
// ...
}
}
```
When explicit type IDs are not provided, generated registration uses computed
numeric IDs (same behavior as other targets).
## Swift
### Output Layout
Swift output is one `.swift` file per schema, for example:
- `<swift_out>/addressbook/addressbook.swift`
### Type Generation
The generator creates Swift models with `@ForyObject` and field IDs.
When package/namespace is non-empty, namespace shaping is controlled by `swift_namespace_style`:
- `enum` (default): nested enum namespace wrappers.
- `flatten`: package-derived prefix on top-level type names (for example `Demo_Foo_User`).
When package/namespace is empty, no enum wrapper or flatten prefix is applied.
For non-empty package with default `enum` style:
```swift
public enum Addressbook {
@ForyObject
public enum Animal: Equatable {
@ForyField(id: 1)
case dog(Addressbook.Dog)
@ForyField(id: 2)
case cat(Addressbook.Cat)
}
@ForyObject
public struct Person: Equatable {
@ForyField(id: 1)
public var name: String = ""
@ForyField(id: 8)
public var pet: Addressbook.Animal = .foryDefault()
}
}
```
For non-empty package with `flatten` style:
```swift
@ForyObject
public struct Addressbook_Person: Equatable { ... }
```
The CLI flag `--swift_namespace_style` overrides schema option `swift_namespace_style` when both are set.
Unions are generated as tagged Swift enums with associated payload values.
Messages with `ref`/`weak_ref` fields are generated as `final class` models to preserve reference semantics.
### Registration
Each schema includes a registration helper with transitive import registration:
```swift
public enum ForyRegistration {
public static func register(_ fory: Fory) throws {
try ComplexPb.ForyRegistration.register(fory)
fory.register(Addressbook.Person.self, id: 100)
fory.register(Addressbook.Animal.self, id: 106)
}
}
```
With non-empty package and `flatten` style, the helper is prefixed too (for example `Addressbook_ForyRegistration`).
For schemas without explicit `[id=...]`, registration uses computed numeric IDs.
If `option enable_auto_type_id = false;` is set, generated code uses name-based registration APIs.
## Cross-Language Notes
### Type ID Behavior
- Explicit `[id=...]` values are used directly in generated registration.
- When type IDs are omitted, generated code uses computed numeric IDs (see `auto_id.*` outputs).
- If `option enable_auto_type_id = false;` is set, generated registration uses namespace/type-name APIs instead of numeric IDs.
### Nested Type Shape
| Language | Nested type form |
| -------- | ------------------------------ |
| Java | `Person.PhoneNumber` |
| Python | `Person.PhoneNumber` |
| Rust | `person::PhoneNumber` |
| C++ | `Person::PhoneNumber` |
| Go | `Person_PhoneNumber` (default) |
| C# | `Person.PhoneNumber` |
| Swift | `Person.PhoneNumber` |
### Byte Helper Naming
| Language | Helpers |
| -------- | ------------------------- |
| Java | `toBytes` / `fromBytes` |
| Python | `to_bytes` / `from_bytes` |
| Rust | `to_bytes` / `from_bytes` |
| C++ | `to_bytes` / `from_bytes` |
| Go | `ToBytes` / `FromBytes` |
| C# | `ToBytes` / `FromBytes` |
| Swift | `toBytes` / `fromBytes` |