This section provides quick examples for getting started with Apache Fory™.
Apache Fory™ has two wire modes:
xlang=false or the equivalent builder option in Java, Scala, Kotlin, Python, C++, Go, and Rust. Use it for same-language traffic because it follows the runtime's native type system, supports a broader language-specific object surface, and is optimized for that runtime.Xlang/default usage uses schema-compatible mode by default. Native mode uses schema-consistent payloads by default unless compatible mode is enabled explicitly.
Use xlang mode when bytes need to cross runtime boundaries. Register custom types with the same numeric ID or namespace/type name on every peer.
Dual-mode runtimes set the xlang option explicitly in the examples below. Dart, JavaScript/TypeScript, C#, and Swift are xlang-only, so their examples do not show an xlang switch.
import org.apache.fory.Fory; public class XlangExample { public record Person(String name, int age) {} public static void main(String[] args) { Fory fory = Fory.builder() .withXlang(true) .build(); fory.register(Person.class, "example", "Person"); Person person = new Person("chaokunyang", 28); byte[] bytes = fory.serialize(person); Person result = (Person) fory.deserialize(bytes); System.out.println(result.name() + " " + result.age()); } }
from dataclasses import dataclass import pyfory @dataclass class Person: name: str age: pyfory.Int32 fory = pyfory.Fory(xlang=True) fory.register(Person, typename="example.Person") person = Person(name="chaokunyang", age=28) data = fory.serialize(person) result = fory.deserialize(data) print(result.name, result.age)
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(); PersonFory.register( fory, Person, namespace: 'example', typeName: 'Person', ); final person = Person() ..name = 'chaokunyang' ..age = 28; final bytes = fory.serialize(person); final result = fory.deserialize<Person>(bytes); print('${result.name} ${result.age}'); }
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))
if err := f.RegisterStruct(Person{}, 1); err != nil {
panic(err)
}
person := &Person{Name: "chaokunyang", Age: 28}
data, err := f.Serialize(person)
if err != nil {
panic(err)
}
var result Person
if err := f.Deserialize(data, &result); err != nil {
panic(err)
}
fmt.Printf("%s %d\n", result.Name, result.Age)
}
use fory::{Error, Fory, ForyObject}; #[derive(ForyObject, Debug, PartialEq)] struct Person { name: String, age: i32, } fn main() -> Result<(), Error> { let mut fory = Fory::builder().xlang(true).build(); fory.register_by_name::<Person>("example", "Person")?; let person = Person { name: "chaokunyang".to_string(), age: 28, }; let bytes = fory.serialize(&person)?; let result: Person = fory.deserialize(&bytes)?; assert_eq!(person, result); Ok(()) }
#include <cassert> #include <string> #include "fory/serialization/fory.h" using namespace fory::serialization; struct Person { std::string name; int32_t age; bool operator==(const Person &other) const { return name == other.name && age == other.age; } FORY_STRUCT(Person, name, age); }; int main() { auto fory = Fory::builder().xlang(true).build(); fory.register_struct<Person>(1); Person person{"chaokunyang", 28}; auto bytes = fory.serialize(person).value(); auto result = fory.deserialize<Person>(bytes).value(); assert(person == result); return 0; }
import org.apache.fory.Fory import org.apache.fory.scala.ForyScala case class Person(name: String, age: Int) object Example { def main(args: Array[String]): Unit = { val fory: Fory = ForyScala.builder() .withXlang(true) .build() fory.register(classOf[Person]) val bytes = fory.serialize(Person("chaokunyang", 28)) val result = fory.deserialize(bytes).asInstanceOf[Person] println(s"${result.name} ${result.age}") } }
import org.apache.fory.ThreadSafeFory import org.apache.fory.kotlin.ForyKotlin data class Person(val name: String, val age: Int) fun main() { val fory: ThreadSafeFory = ForyKotlin.builder() .withXlang(true) .requireClassRegistration(true) .buildThreadSafeFory() fory.register(Person::class.java) val bytes = fory.serialize(Person("chaokunyang", 28)) val result = fory.deserialize(bytes) as Person println("${result.name} ${result.age}") }
import Fory, { Type } from "@apache-fory/core"; const personType = Type.struct( { typeName: "example.Person" }, { name: Type.string(), age: Type.int32(), }, ); const fory = new Fory(); const { serialize, deserialize } = fory.register(personType); const payload = serialize({ name: "chaokunyang", age: 28 }); const result = deserialize(payload); console.log(result);
using Apache.Fory; [ForyObject] public sealed class Person { public string Name { get; set; } = string.Empty; public int Age { get; set; } } Fory fory = Fory.Builder().Build(); fory.Register<Person>(1); Person person = new() { Name = "chaokunyang", Age = 28 }; byte[] data = fory.Serialize(person); Person result = fory.Deserialize<Person>(data); Console.WriteLine($"{result.Name} {result.Age}");
import Fory @ForyStruct struct Person: Equatable { var name: String = "" var age: Int32 = 0 } let fory = Fory() fory.register(Person.self, id: 1) let person = Person(name: "chaokunyang", age: 28) let data = try fory.serialize(person) let result: Person = try fory.deserialize(data) print("\(result.name) \(result.age)")
For more cross-language rules and examples, see:
Use native mode only when every reader and writer is the same runtime family. Native mode supports broader language-specific object models than portable xlang mappings and is optimized for the owning runtime.
Java and Python native modes are first-class same-language entry points. Use Java native mode when replacing JDK serialization, Kryo, FST, Hessian, or Java-only Protocol Buffers payloads. Use Python native mode when replacing pickle or cloudpickle for Python-only payloads.
Dart, JavaScript/TypeScript, C#, and Swift do not expose native mode.
Fory fory = Fory.builder() .withXlang(false) .requireClassRegistration(true) .build();
Register Java classes and use serialize / deserialize as usual. See the Java Guide for Java object hooks, Externalizable, dynamic object graphs, object copy, and Java native-mode zero-copy buffers.
import pyfory fory = pyfory.Fory(xlang=False, ref=False, strict=True)
Register Python classes and use serialize / deserialize as usual. See the Python Guide for native-mode pickle replacement behavior and security settings.
f := fory.New(fory.WithXlang(false))
Use native mode for Go-only structs, pointers, interfaces, and Go-specific type behavior. See the Go Guide for struct tags and native-mode configuration.
let mut fory = Fory::builder().xlang(false).build();
Use native mode for Rust-only payloads that rely on Rust-specific object behavior. See the Rust Guide for derive, references, and supported types.
auto fory = Fory::builder().xlang(false).build();
Use native mode for C++-only traffic that does not need portable xlang type mappings. See the C++ Guide for FORY_STRUCT, configuration, and schema metadata.
val fory = ForyScala.builder() .withXlang(false) .build()
Use native mode for Scala/JVM-only traffic that needs Scala case classes, collections, tuples, options, or enums on the JVM runtime path. See the Scala Guide.
val fory = ForyKotlin.builder() .withXlang(false) .requireClassRegistration(true) .buildThreadSafeFory()
Use native mode for Kotlin/JVM-only traffic that needs Kotlin data classes, nullable types, ranges, unsigned values, or Kotlin collections on the JVM runtime path. See the Kotlin Guide.
Row format provides zero-copy random access to serialized data, making it ideal for analytics workloads and data processing pipelines.
import org.apache.fory.format.*; import java.util.*; import java.util.stream.*; public class Bar { String f1; List<Long> f2; } public class Foo { int f1; List<Integer> f2; Map<String, Integer> f3; List<Bar> f4; } RowEncoder<Foo> encoder = Encoders.bean(Foo.class); Foo foo = new Foo(); foo.f1 = 10; foo.f2 = IntStream.range(0, 1000000).boxed().collect(Collectors.toList()); foo.f3 = IntStream.range(0, 1000000).boxed().collect(Collectors.toMap(i -> "k"+i, i -> i)); List<Bar> bars = new ArrayList<>(1000000); for (int i = 0; i < 1000000; i++) { Bar bar = new Bar(); bar.f1 = "s" + i; bar.f2 = LongStream.range(0, 10).boxed().collect(Collectors.toList()); bars.add(bar); } foo.f4 = bars; // Serialize to row format (can be zero-copy read by Python) BinaryRow binaryRow = encoder.toRow(foo); // Deserialize entire object Foo newFoo = encoder.fromRow(binaryRow); // Zero-copy access to nested fields without full deserialization BinaryArray binaryArray2 = binaryRow.getArray(1); // Access f2 field BinaryArray binaryArray4 = binaryRow.getArray(3); // Access f4 field BinaryRow barStruct = binaryArray4.getStruct(10); // Access 11th Bar element long value = barStruct.getArray(1).getInt64(5); // Access nested value // Partial deserialization RowEncoder<Bar> barEncoder = Encoders.bean(Bar.class); Bar newBar = barEncoder.fromRow(barStruct); Bar newBar2 = barEncoder.fromRow(binaryArray4.getStruct(20));
from dataclasses import dataclass from typing import List, Dict import pyarrow as pa import pyfory @dataclass class Bar: f1: str f2: List[pa.int64] @dataclass class Foo: f1: pa.int32 f2: List[pa.int32] f3: Dict[str, pa.int32] f4: List[Bar] encoder = pyfory.encoder(Foo) foo = Foo( f1=10, f2=list(range(1000_000)), f3={f"k{i}": i for i in range(1000_000)}, f4=[Bar(f1=f"s{i}", f2=list(range(10))) for i in range(1000_000)] ) # Serialize to row format binary: bytes = encoder.to_row(foo).to_bytes() # Zero-copy random access without full deserialization foo_row = pyfory.RowData(encoder.schema, binary) print(foo_row.f2[100000]) # Access element directly print(foo_row.f4[100000].f1) # Access nested field print(foo_row.f4[200000].f2[5]) # Access deeply nested field
For more details on row format, see Java Row Format Guide or Python Row Format Guide.