Apache Fory™ is a blazingly fast multi-language serialization framework for idiomatic domain objects, schema IDL, and cross-language data exchange.
Important
Apache Fory™ was previously named Apache Fury. For versions before 0.11, use
fury instead of fory in package names, imports, and dependencies. See the
Fury docs for older
releases.
Fory is built for fast, compact serialization across languages and runtimes. It works with idiomatic objects in each language, supports shared schemas when you need a contract, and preserves object features such as shared and circular references.
- Efficient Cross-Language Encoding: Exchange payloads across supported languages with compact binary encoding, metadata packing, schema evolution, shared/circular references, and polymorphic runtime types.
- Domain Objects First: Serialize Java classes, Python dataclasses, Go structs, Rust/C++ structs, and generated or annotated model types directly. Preserve shared and circular references when object identity matters.
- Reference-Aware Schema IDL: Support shared and circular references directly in the schema, alongside numbers, strings, lists, maps, arrays, enums, structs, and unions. Define schemas once, then generate native domain objects for each language without forcing wrapper types into user code.
- Row-Format Random Access: Read fields, arrays, and nested values without rebuilding full objects, with zero-copy access, partial reads, and Arrow integration.
- Optimized Runtimes: Java JIT serializers and generated/static serializers in other runtimes keep hot paths fast and payloads compact.
- Language And Platform Support: Java, Python, C++, Go, Rust, JavaScript/TypeScript, C#, Swift, Dart, Scala, and Kotlin, including GraalVM native image, Dart VM/Flutter/web, and Node.js/browser JavaScript.
Benchmarks show Fory delivering higher throughput and smaller serialized payloads than common serialization frameworks on representative workloads. Java has the broadest comparison set; the other charts show runtime-specific results across supported languages.
Java Benchmarks
In Java serialization benchmarks, Fory reaches up to 170x the throughput of JDK serialization on selected workloads.
Python Benchmarks
Rust Benchmarks
Benchmarks for C++, Go, JavaScript/TypeScript, C#, Swift, and Dart
C++ Benchmarks
Go Benchmarks
JavaScript/TypeScript Benchmarks
C# Benchmarks
Swift Benchmarks
Dart Benchmarks
Pick the runtime you use and run the package-manager command, or paste the dependency block into your build file.
Java
Maven:
<dependency>
<groupId>org.apache.fory</groupId>
<artifactId>fory-core</artifactId>
<version>0.17.0</version>
</dependency>Gradle:
implementation "org.apache.fory:fory-core:0.17.0"Scala
sbt:
libraryDependencies += "org.apache.fory" %% "fory-scala" % "0.17.0"Kotlin
Gradle:
implementation("org.apache.fory:fory-kotlin:0.17.0")Maven:
<dependency>
<groupId>org.apache.fory</groupId>
<artifactId>fory-kotlin</artifactId>
<version>0.17.0</version>
</dependency>Python
pip install pyforyFor row-format support:
pip install "pyfory[format]"Rust
Cargo.toml:
[dependencies]
fory = "0.17"C++
CMake:
include(FetchContent)
FetchContent_Declare(
fory
GIT_REPOSITORY https://github.com/apache/fory.git
GIT_TAG v0.17.0
SOURCE_SUBDIR cpp
)
FetchContent_MakeAvailable(fory)
target_link_libraries(my_app PRIVATE fory::serialization)Bazel:
# MODULE.bazel
bazel_dep(name = "fory", version = "0.17.0")
git_override(module_name = "fory", remote = "https://github.com/apache/fory.git", commit = "v0.17.0")
# BUILD
deps = ["@fory//cpp/fory/serialization:fory_serialization"]See the C++ installation guide for complete CMake, Bazel, and source-build details.
Go
go get github.com/apache/fory/go/foryJavaScript/TypeScript
npm install @apache-fory/coreFor the Node.js string fast path:
npm install @apache-fory/core @apache-fory/hpsC#
dotnet add package Apache.Fory --version 0.17.0Dart
dart pub add fory:^0.17.0
dart pub add dev:build_runnerSwift
Add Fory to Package.swift:
dependencies: [
.package(url: "https://github.com/apache/fory.git", exact: "0.17.0")
],
targets: [
.target(
name: "YourTarget",
dependencies: [.product(name: "Fory", package: "fory")]
)
]See the Swift guide for generated serializer setup.
Development From Source
See docs/DEVELOPMENT.md.
Snapshots for Java, Scala, and Kotlin are available from
https://repository.apache.org/snapshots/ with the matching -SNAPSHOT version.
| Mode | Use it when | Start here |
|---|---|---|
| Xlang serialization | Data crosses language boundaries | Cross-language guide |
| Native serialization | Producer and consumer are in the same language | Language guide for your runtime |
| Row format | You need random field access or analytics-style partial reads | Row format spec |
Use native mode for same-language traffic. It avoids xlang's cross-language type mapping and metadata constraints, so it can serialize broader language-specific object graphs and is the fastest path for same-language payloads.
Compatible mode is Fory's schema-evolution mode. It writes the metadata readers and writers need to tolerate schema differences. Xlang mode enables compatible mode by default to better handle differences between language type systems. Native mode keeps it off by default for smaller payloads and higher throughput.
Use compatible mode when services deploy independently or when fields may be added or deleted over time. Use schema-consistent mode when writer and reader schemas deploy together and you want the smallest payloads.
For xlang, all peers must agree on type identity. Name-based registration is easier to read in examples. Numeric IDs are smaller and faster, but they require coordination across every reader and writer.
Xlang mode writes the cross-language Fory wire format. Bytes produced by one runtime can be read by another when the runtimes use the same type identity, compatible mode setting, and field schema.
Java
import org.apache.fory.Fory;
public class Example {
public static class Person {
public String name;
public int age;
}
public static void main(String[] args) {
Fory fory = Fory.builder().withXlang(true).withCompatible(true).build();
fory.register(Person.class, "example.Person");
Person person = new Person();
person.name = "Alice";
person.age = 30;
byte[] bytes = fory.serialize(person);
Person decoded = (Person) fory.deserialize(bytes);
System.out.println(decoded.name);
}
}Python
from dataclasses import dataclass
import pyfory
@dataclass
class Person:
name: str
age: pyfory.Int32
fory = pyfory.Fory(xlang=True, compatible=True)
fory.register_type(Person, typename="example.Person")
data = fory.serialize(Person("Alice", 30))
person = fory.deserialize(data)
print(person.name)Go
package main
import (
"fmt"
"github.com/apache/fory/go/fory"
)
type Person struct {
Name string
Age int32
}
func main() {
f := fory.New(fory.WithXlang(true), fory.WithCompatible(true))
if err := f.RegisterStructByName(Person{}, "example.Person"); err != nil {
panic(err)
}
data, _ := f.Serialize(&Person{Name: "Alice", Age: 30})
var person Person
if err := f.Deserialize(data, &person); err != nil {
panic(err)
}
fmt.Println(person.Name)
}Rust
use fory::{Error, Fory, ForyStruct};
#[derive(ForyStruct, Debug, PartialEq)]
struct Person {
name: String,
age: i32,
}
fn main() -> Result<(), Error> {
let mut fory = Fory::builder().xlang(true).compatible(true).build();
fory.register_by_name::<Person>("example", "Person")?;
let bytes = fory.serialize(&Person {
name: "Alice".to_string(),
age: 30,
})?;
let person: Person = fory.deserialize(&bytes)?;
println!("{}", person.name);
Ok(())
}C++
#include "fory/serialization/fory.h"
#include <cstdint>
#include <iostream>
#include <string>
using namespace fory::serialization;
struct Person {
std::string name;
int32_t age;
};
FORY_STRUCT(Person, name, age);
int main() {
auto fory = Fory::builder().xlang(true).compatible(true).build();
fory.register_struct<Person>("example.Person");
auto bytes = fory.serialize(Person{"Alice", 30}).value();
Person person = fory.deserialize<Person>(bytes).value();
std::cout << person.name << std::endl;
}JavaScript/TypeScript
import Fory, { Type } from "@apache-fory/core";
const personType = Type.struct(
{ typeName: "example.Person" },
{
name: Type.string(),
age: Type.int32(),
},
);
const fory = new Fory({ compatible: true });
const { serialize, deserialize } = fory.register(personType);
const bytes = serialize({ name: "Alice", age: 30 });
const person = deserialize(bytes);
console.log(person.name);C#
using Apache.Fory;
[ForyObject]
public sealed class Person
{
public string Name { get; set; } = string.Empty;
public int Age { get; set; }
}
Fory fory = Fory.Builder()
.Compatible(true)
.Build();
fory.Register<Person>("example", "Person");
byte[] bytes = fory.Serialize(new Person { Name = "Alice", Age = 30 });
Person person = fory.Deserialize<Person>(bytes);
Console.WriteLine(person.Name);C# always writes the xlang frame header, so there is no separate xlang builder flag.
Dart
import 'package:fory/fory.dart';
part 'person.fory.dart';
@ForyStruct()
class Person {
Person();
String name = '';
@ForyField(type: Int32Type())
int age = 0;
}
void main() {
final fory = Fory(compatible: true);
PersonFory.register(
fory,
Person,
namespace: 'example',
typeName: 'Person',
);
final bytes = fory.serialize(Person()
..name = 'Alice'
..age = 30);
final person = fory.deserialize<Person>(bytes);
print(person.name);
}Dart uses the xlang wire format directly. Generate the companion file before running:
dart run build_runner build --delete-conflicting-outputsSwift
import Fory
@ForyStruct
struct Person {
var name: String = ""
var age: Int32 = 0
}
let fory = Fory(xlang: true, compatible: true)
try fory.register(Person.self, namespace: "example", name: "Person")
let bytes = try fory.serialize(Person(name: "Alice", age: 30))
let person: Person = try fory.deserialize(bytes)
print(person.name)Scala
import org.apache.fory.Fory
import org.apache.fory.serializer.scala.ScalaSerializers
case class Person(name: String, age: Int)
val fory = Fory.builder()
.withXlang(true)
.withCompatible(true)
.build()
ScalaSerializers.registerSerializers(fory)
fory.register(classOf[Person], "example.Person")
val bytes = fory.serialize(Person("Alice", 30))
val person = fory.deserialize(bytes).asInstanceOf[Person]
println(person.name)Kotlin
import org.apache.fory.Fory
import org.apache.fory.serializer.kotlin.KotlinSerializers
data class Person(val name: String, val age: Int)
fun main() {
val fory = Fory.builder()
.withXlang(true)
.withCompatible(true)
.build()
KotlinSerializers.registerSerializers(fory)
fory.register(Person::class.java, "example.Person")
val bytes = fory.serialize(Person("Alice", 30))
val person = fory.deserialize(bytes) as Person
println(person.name)
}For shared/circular references, polymorphism, numeric IDs versus names, and type-mapping rules, see the cross-language guide and type mapping specification.
Use native mode when the writer and reader are in the same language. Java and
Python can serialize broader language-specific object graphs this way. The
languages below expose an explicit xlang=false or native-mode setting; runtimes
without that switch stay on their documented default path.
Keep class/type registration enabled for untrusted input. See the language guides for runtime-specific security and compatibility settings.
Java
Fory fory = Fory.builder()
.withXlang(false)
.requireClassRegistration(true)
.build();
// Register, serialize, and deserialize as in the xlang example above.Python
fory = pyfory.Fory(xlang=False, ref=True)
# Register, serialize, and deserialize as in the xlang example above.Go
f := fory.New(fory.WithXlang(false))
// Register, serialize, and deserialize as in the xlang example above.Rust
let mut fory = Fory::builder().xlang(false).build();
// Register, serialize, and deserialize as in the xlang example above.C++
auto fory = Fory::builder().xlang(false).build();
// Register, serialize, and deserialize as in the xlang example above.Scala
val fory = Fory.builder()
.withXlang(false)
.requireClassRegistration(true)
.build()
ScalaSerializers.registerSerializers(fory)
// Register, serialize, and deserialize as in the xlang example above.Kotlin
val fory = Fory.builder()
.withXlang(false)
.requireClassRegistration(true)
.build()
KotlinSerializers.registerSerializers(fory)
// Register, serialize, and deserialize as in the xlang example above.Fory IDL is Fory's schema language for shared data models. It supports references, nullable fields, lists, maps, arrays, enums, messages, and unions, and generates native data structures for supported languages. Use it when multiple languages need one shared contract.
package tree;
message TreeNode {
string id = 1;
string name = 2;
list<ref TreeNode> children = 3;
ref(weak=true) TreeNode parent = 4; // back-pointer
}See the Fory IDL and compiler guide.
Row format is for random access and partial reads. These examples encode an object with an integer array field, then read one array element from the binary row without rebuilding the object.
Python
from dataclasses import dataclass
from typing import List
import pyfory
@dataclass
class User:
id: pyfory.Int32
name: str
scores: List[pyfory.Int32]
encoder = pyfory.encoder(User)
binary = encoder.to_row(User(1, "Alice", [98, 100, 95])).to_bytes()
row = pyfory.RowData(encoder.schema, binary)
print(row.name)
print(row.scores[1])Java
public class User {
public int id;
public String name;
public int[] scores;
}
RowEncoder<User> encoder = Encoders.bean(User.class);
User user = new User();
user.id = 1;
user.name = "Alice";
user.scores = new int[] {98, 100, 95};
BinaryRow row = encoder.toRow(user);
Schema schema = encoder.schema();
Schema.StringField nameField = schema.stringField("name");
Schema.ArrayField scoresField = schema.arrayField("scores");
String name = nameField.get(row);
ArrayData scores = scoresField.get(row);
int secondScore = scores.getInt32(1);For Java imports, nested structs, arrays/maps, Arrow integration, and partial deserialization, see the Java row-format guide, the Python row-format guide, and the row-format specification.
User Guides
| Guide | Source | Website |
|---|---|---|
| Java | docs/guide/java | View |
| Python | docs/guide/python | View |
| Rust | docs/guide/rust | View |
| C++ | docs/guide/cpp | View |
| Go | docs/guide/go | View |
| JavaScript/TypeScript | docs/guide/javascript | View |
| C# | docs/guide/csharp | View |
| Swift | docs/guide/swift | View |
| Dart | docs/guide/dart | View |
| Scala | docs/guide/scala | View |
| Kotlin | docs/guide/kotlin | View |
| Cross-language xlang | docs/guide/xlang | View |
| Schema IDL/compiler | docs/compiler | View |
| GraalVM native image | docs/guide/java/graalvm-support.md | View |
| Development | docs/DEVELOPMENT.md | View |
Specifications
| Specification | Source | Website |
|---|---|---|
| Xlang serialization | xlang_serialization_spec.md | View |
| Java serialization | java_serialization_spec.md | View |
| Row format | row_format_spec.md | View |
| Cross-language mapping | xlang_type_mapping.md | View |
- Slack workspace
- @ApacheFory on X
- GitHub issues
- Apache Fory mailing lists for development discussion
Read CONTRIBUTING.md and docs/DEVELOPMENT.md before sending pull requests. Bug reports, docs fixes, tests, benchmarks, and runtime improvements are welcome.
Apache Fory™ is licensed under the Apache License 2.0.











