Skip to content

apache/fory

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

2,018 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
Apache Fory logo

Build Status Slack Channel X Maven Version Crates.io PyPI npm NuGet pub.dev

Apache Fory™ is a blazingly fast multi-language serialization framework for idiomatic domain objects, schema IDL, and cross-language data exchange.

https://fory.apache.org

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.

Why Fory

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.

Performance

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.

Java serialization throughput

Java deserialization throughput

Java xlang throughput

Python Benchmarks

Python serialization throughput

Rust Benchmarks

Rust serialization throughput

Benchmarks for C++, Go, JavaScript/TypeScript, C#, Swift, and Dart

C++ Benchmarks

C++ serialization throughput

Go Benchmarks

Go serialization throughput

JavaScript/TypeScript Benchmarks

JavaScript serialization throughput

C# Benchmarks

C# serialization throughput

Swift Benchmarks

Swift serialization throughput

Dart Benchmarks

Dart serialization throughput

Installation

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 pyfory

For 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/fory

JavaScript/TypeScript

npm install @apache-fory/core

For the Node.js string fast path:

npm install @apache-fory/core @apache-fory/hps

C#

dotnet add package Apache.Fory --version 0.17.0

Dart

dart pub add fory:^0.17.0
dart pub add dev:build_runner

Swift

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.

Choose Serialization Mode

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.

Cross-Language Serialization

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-outputs

Swift

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.

Native Serialization

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.

Schema IDL

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

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.

Documentation

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

Community

Contributing

Read CONTRIBUTING.md and docs/DEVELOPMENT.md before sending pull requests. Bug reports, docs fixes, tests, benchmarks, and runtime improvements are welcome.

License

Apache Fory™ is licensed under the Apache License 2.0.