跳到主要内容
版本:dev

Basic Serialization

This page covers basic object graph serialization and supported types.

Object Graph Serialization

Apache Fory™ provides automatic serialization of complex object graphs, preserving the structure and relationships between objects. The #[derive(ForyStruct)] macro generates efficient serialization code at compile time, eliminating reflection overhead.

Key capabilities:

  • Nested struct serialization with arbitrary depth
  • Collection types (Vec, HashMap, HashSet, BTreeMap)
  • Optional fields with Option<T>
  • Automatic handling of primitive types and strings
  • Efficient binary encoding with variable-length integers
use fory::{Fory, Error};
use fory::ForyStruct;
use std::collections::HashMap;

#[derive(ForyStruct, Debug, PartialEq)]
struct Person {
name: String,
age: i32,
address: Address,
hobbies: Vec<String>,
metadata: HashMap<String, String>,
}

#[derive(ForyStruct, Debug, PartialEq)]
struct Address {
street: String,
city: String,
country: String,
}

let mut fory = Fory::builder().xlang(true).build();
fory.register_by_name::<Address>("example.Address").unwrap();
fory.register_by_name::<Person>("example.Person").unwrap();

let person = Person {
name: "John Doe".to_string(),
age: 30,
address: Address {
street: "123 Main St".to_string(),
city: "New York".to_string(),
country: "USA".to_string(),
},
hobbies: vec!["reading".to_string(), "coding".to_string()],
metadata: HashMap::from([
("role".to_string(), "developer".to_string()),
]),
};

let bytes = fory.serialize(&person).unwrap();
let decoded: Person = fory.deserialize(&bytes)?;
assert_eq!(person, decoded);

Supported Types

Primitive Types

Rust TypeDescription
boolBoolean
i8, i16, i32, i64Signed integers
f32, f64Floating point
BFloat1616-bit brain floating point
StringUTF-8 string

Collections

Rust TypeDescription
Vec<T>Dynamic array
VecDeque<T>Double-ended queue
LinkedList<T>Doubly-linked list
HashMap<K, V>Hash map
BTreeMap<K, V>Ordered map
HashSet<T>Hash set
BTreeSet<T>Ordered set
BinaryHeap<T>Binary heap
Option<T>Optional value

Vec<BFloat16> is the dense carrier when the schema is array<bfloat16>.

Smart Pointers

Rust TypeDescription
Box<T>Heap allocation
Rc<T>Reference counting (shared refs tracked)
Arc<T>Thread-safe reference counting (shared refs tracked)
RcWeak<T>Weak reference to Rc<T> (breaks circular refs)
ArcWeak<T>Weak reference to Arc<T> (breaks circular refs)
RefCell<T>Interior mutability (runtime borrow checking)
Mutex<T>Thread-safe interior mutability

Date and Time

Rust TypeDescription
DateDate without timezone, stored as epoch days
TimestampPoint in time, stored as epoch seconds and nanos
DurationSigned duration, stored as seconds and normalized nanos

The built-in carriers expose dependency-free constructors, accessors, conversions, and checked arithmetic:

use fory::{Date, Duration, Timestamp};

let date = Date::from_epoch_days(19_782);
assert_eq!(date.checked_add_days(1)?.epoch_days(), 19_783);

let timestamp = Timestamp::from_epoch_millis(-1);
assert_eq!(timestamp.to_epoch_millis()?, -1);

let duration = Duration::from_parts(1, 1_500_000_000)?;
assert_eq!(duration.to_millis()?, 2_500);
let later = timestamp.checked_add_duration(duration)?;

chrono::NaiveDate, chrono::NaiveDateTime, and chrono::Duration are supported when the Rust chrono feature is enabled:

[dependencies]
fory = { version = "1.5.0", features = ["chrono"] }

Custom Types

Use #[derive(ForyStruct)] for object graph serialization. The separate Rust Row Format guide documents #[derive(ForyRow)] and its supported type set.

Serialization APIs

use fory::{Fory, Reader};

let mut fory = Fory::builder().xlang(true).build();
fory.register::<MyStruct>(1)?;

let obj = MyStruct { /* ... */ };

// Basic serialize/deserialize
let bytes = fory.serialize(&obj)?;
let decoded: MyStruct = fory.deserialize(&bytes)?;

// Serialize to existing buffer
let mut buf: Vec<u8> = vec![];
fory.serialize_to(&mut buf, &obj)?;

// Deserialize from reader
let mut reader = Reader::new(&buf);
let decoded: MyStruct = fory.deserialize_from(&mut reader)?;

When the Rust value type uses an external structural serializer or custom serializer, select it explicitly at the root:

let bytes = fory.serialize_with::<UserSerializer>(&user)?;
let decoded: third_party::User =
fory.deserialize_with::<UserSerializer>(&bytes)?;

Carrier serializers compose the same selection for a root container:

use fory::VecSerializer;

let bytes =
fory.serialize_with::<VecSerializer<UserSerializer>>(&users)?;
let decoded: Vec<third_party::User> =
fory.deserialize_with::<VecSerializer<UserSerializer>>(&bytes)?;

See External-Type Serialization for field annotations, all supported carriers, and registration.

Performance Tips

  • Buffer Pre-allocation: Minimizes memory allocations during serialization
  • Compact Encoding: Variable-length encoding for space efficiency
  • Little-Endian: Optimized for modern CPU architectures
  • Reference Deduplication: Shared objects serialized only once