The FDL compiler generates cross-language serialization code from schema definitions. It enables type-safe cross-language data exchange by generating native data structures with Fory serialization support for multiple programming languages.
- Multi-language code generation: Java, Python, Go, Rust, C++, C#, JavaScript, Swift, Dart, Scala, and Kotlin
- Rich type system: Primitives, enums, messages, lists, dense arrays, maps
- Cross-language serialization: Generated code works seamlessly with Apache Fory
- Type ID and namespace support: Both numeric IDs and name-based type registration
- Field modifiers: Optional fields, reference tracking, list fields, scalar encoding modifiers
- File imports: Modular schemas with import support
For comprehensive documentation, see the FDL Schema Guide:
- FDL Syntax Reference - Complete language syntax and grammar
- Type System - Primitive types, collections, and language mappings
- Compiler Guide - CLI options and build integration
- Generated Code - Output format for each target language
- Protocol Buffers vs FDL - Feature comparison and porting guide
cd compiler
pip install -e .Create a .fdl file:
package demo;
enum Color [id=101] {
GREEN = 0;
RED = 1;
BLUE = 2;
}
message Dog [id=102] {
optional string name = 1;
int32 age = 2;
}
message Cat [id=103] {
ref Dog friend = 1;
optional string name = 2;
list<string> tags = 3;
map<string, int32> scores = 4;
int32 lives = 5;
}
# Generate for all languages
foryc schema.fdl --output ./generated
# Generate for specific languages
foryc schema.fdl --lang java,python,csharp,javascript,scala --output ./generated
# Language-specific output directories (protoc-style)
foryc schema.fdl --java_out=./src/main/java --python_out=./python/src --csharp_out=./csharp/src/Generated --javascript_out=./javascript --scala_out=./scala/src/main/scala
# Combine with other options
foryc schema.fdl --java_out=./gen --go_out=./gen/go --csharp_out=./gen/csharp --javascript_out=./gen/js --scala_out=./gen/scala -I ./protoJava:
import demo.*;
import org.apache.fory.Fory;
Fory fory = Fory.builder()
.withXlang(true)
.withRefTracking(true)
.withModule(DemoForyModule.INSTANCE)
.build();
Cat cat = new Cat();
cat.setName("Whiskers");
cat.setLives(9);
byte[] bytes = fory.serialize(cat);Python:
import pyfory
from demo import Cat, register_demo_types
fory = pyfory.Fory(xlang=True)
register_demo_types(fory)
cat = Cat(name="Whiskers", lives=9)
data = fory.serialize(cat)package com.example.models;
Import types from other FDL files:
import "common/types.fdl";
import "models/address.fdl";
Imports are resolved relative to the importing file. All types from imported files become available for use in the current file.
Example:
// common.fdl
package common;
message Address [id=100] {
string street = 1;
string city = 2;
}
// user.fdl
package user;
import "common.fdl";
message User [id=101] {
string name = 1;
Address address = 2; // Uses imported type
}
enum Status [id=100] {
PENDING = 0;
ACTIVE = 1;
INACTIVE = 2;
}
message User [id=101] {
string name = 1;
int32 age = 2;
optional string email = 3;
}
Types can have options specified in brackets after the name:
message User [id=101] { ... } // Registered with type ID 101
message User [id=101, deprecated=true] { ... } // Multiple options
Types without [id=...] use name-based registration:
message Config { ... } // Registered as "package.Config"
| FDL Type | Java | Python | Go | Rust | C++ | C# | JavaScript |
|---|---|---|---|---|---|---|---|
bool |
boolean |
bool |
bool |
bool |
bool |
bool |
boolean |
int8 |
byte |
pyfory.Int8 |
int8 |
i8 |
int8_t |
sbyte |
number |
int16 |
short |
pyfory.Int16 |
int16 |
i16 |
int16_t |
short |
number |
int32 |
int |
pyfory.Int32 |
int32 |
i32 |
int32_t |
int |
number |
int64 |
long |
pyfory.Int64 |
int64 |
i64 |
int64_t |
long |
bigint | number |
float16 |
Float16 |
pyfory.Float16 |
float16 |
Float16 |
fory::float16_t |
Half |
number |
bfloat16 |
BFloat16 |
pyfory.BFloat16 |
bfloat16 |
BFloat16 |
fory::bfloat16_t |
BFloat16 |
number |
float32 |
float |
pyfory.Float32 |
float32 |
f32 |
float |
float |
number |
float64 |
double |
pyfory.Float64 |
float64 |
f64 |
double |
double |
number |
string |
String |
str |
string |
String |
std::string |
string |
string |
bytes |
byte[] |
bytes |
[]byte |
Vec<u8> |
std::vector<uint8_t> |
byte[] |
Uint8Array |
date |
LocalDate |
datetime.date |
time.Time |
fory::Date |
fory::Date |
DateOnly |
Date |
timestamp |
Instant |
datetime.datetime |
time.Time |
fory::Timestamp |
fory::Timestamp |
DateTimeOffset |
Date |
list<string> tags = 1; // List<String>
array<int32> dense_numbers = 2; // Packed dense int32 array
map<string, fixed int32> scores = 3; // Map<String, fixed-width Integer>
optional: Field can be null/Noneref: Enable reference tracking for shared/circular referenceslist<T>: Ordered collection schema (alias:repeated T)array<T>: Dense numeric/vector schema
message Example {
optional string nullable_field = 1;
ref OtherMessage shared_ref = 2;
list<int32> numbers = 3;
list<fixed int32> offsets = 4;
array<float32> embedding = 5;
}
FDL uses plain option keys without a (fory) prefix:
File-level options:
option use_record_for_java_message = true;
option polymorphism = true;
option enable_auto_type_id = true;
enable_auto_type_id defaults to true. Set it to false to keep name-based registration
for types that omit explicit IDs.
Message/Enum options:
message MyMessage [id=100] {
option evolving = false;
option use_record_for_java = true;
string name = 1;
}
enum Status [id=101] {
UNKNOWN = 0;
ACTIVE = 1;
}
Field options:
message Example {
ref MyType friend = 1;
string nickname = 2 [nullable=true];
ref MyType data = 3 [nullable=true];
ref(weak=true) MyType parent = 4;
}
fory_compiler/
├── __init__.py # Package exports
├── __main__.py # Module entry point
├── cli.py # Command-line interface
├── frontend/
│ └── fdl/
│ ├── __init__.py
│ ├── lexer.py # Hand-written tokenizer
│ └── parser.py # Recursive descent parser
├── ir/
│ ├── __init__.py
│ ├── ast.py # Canonical Fory IDL AST
│ ├── validator.py # Schema validation
│ └── emitter.py # Optional FDL emitter
└── generators/
├── base.py # Base generator class
├── java.py # Java POJO generator
├── python.py # Python dataclass generator
├── go.py # Go struct generator
├── rust.py # Rust struct generator
├── cpp.py # C++ struct generator
├── csharp.py # C# class generator
└── javascript.py # JavaScript interface generator
The FDL frontend is a hand-written lexer/parser that produces the Fory IDL AST:
- Lexer (
frontend/fdl/lexer.py): Tokenizes FDL source into tokens - Parser (
frontend/fdl/parser.py): Builds the AST from the token stream - AST (
ir/ast.py): Canonical node types -Schema,Message,Enum,Field,FieldType
Each generator extends BaseGenerator and implements:
generate(): Returns list ofGeneratedFileobjectsgenerate_type(): Converts FDL types to target language types- Language-specific registration helpers or modules
Generates POJOs with:
- Private fields with getters/setters
@Nullableannotations for nullable fields and@Refannotations for ref fields- Schema module class
public class Cat {
@Ref
private Dog friend;
@Nullable
private String name;
private List<String> tags;
// ...
}Generates dataclasses with:
- Type hints
- Default values
- Registration function
@dataclass
class Cat:
friend: Optional[Dog] = None
name: Optional[str] = None
tags: List[str] = NoneGenerates structs with:
- Fory struct tags
- Pointer types for nullable fields
- Registration function with error handling
type Cat struct {
Friend *Dog `fory:"ref"`
Name *string `fory:"nullable"`
Tags []string
}Generates structs with:
#[derive(ForyStruct)],#[derive(ForyEnum)], and#[derive(ForyUnion)]macros#[fory(...)]field attributes- a registration helper for name-based registration
#[derive(ForyStruct, Debug, Clone, PartialEq, Default)]
pub struct Cat {
pub friend: Arc<Dog>,
#[fory(nullable = true)]
pub name: Option<String>,
pub tags: Vec<String>,
}Generates structs with:
FORY_STRUCTmacro for serializationstd::optionalfor nullable fieldsstd::shared_ptrfor ref fields
struct Cat {
std::shared_ptr<Dog> friend;
std::optional<std::string> name;
std::vector<std::string> tags;
int32_t scores;
int32_t lives;
FORY_STRUCT(Cat, friend, name, tags, scores, lives);
};Generates classes with:
[ForyStruct],[ForyEnum], and[ForyUnion]model attributes- Auto-properties for schema fields
- Registration helper class and
ToBytes/FromByteshelpers
[ForyStruct]
public sealed partial class Cat
{
public Dog? Friend { get; set; }
public string Name { get; set; } = string.Empty;
public List<string> Tags { get; set; } = new();
}For full C# IDL verification (including root cross-package imports and file-based roundtrip paths), run:
cd integration_tests/idl_tests
./run_csharp_tests.shGenerates interfaces with:
export interfacedeclarations for messagesexport enumdeclarations for enums- Discriminated unions with case enums
- Registration helper function
export interface Cat {
friend?: Dog | null;
name?: string | null;
tags: string[];
scores: Map<string, number>;
lives: number;
}foryc [OPTIONS] FILES...
Arguments:
FILES FDL files to compile
Options:
--lang TEXT Target languages (java,python,cpp,rust,go,csharp,javascript,swift,dart,scala or "all")
Default: all
--output, -o PATH Output directory
Default: ./generated
--help Show help message
See the examples/ directory for sample FDL files and generated output.
# Compile the demo schema
foryc examples/demo.fdl --output examples/generated# Install in development mode
pip install -e .
# Run the compiler
python -m fory_compiler compile examples/demo.fdl
# Or use the installed command
foryc examples/demo.fdlApache License 2.0