Custom Serializers
For types that don't support #[derive(ForyObject)], implement the Serializer trait manually.
When to Use Custom Serializers
- External types from other crates
- Types with special serialization requirements
- Legacy data format compatibility
- Performance-critical custom encoding
Implementing the Serializer Trait
use fory::{Fory, ReadContext, WriteContext, Serializer, ForyDefault, Error};
use std::any::Any;
#[derive(Debug, PartialEq)]
struct CustomType {
value: i32,
name: String,
}
impl Serializer for CustomType {
fn fory_write_data(&self, context: &mut WriteContext, is_field: bool) {
context.writer.write_i32(self.value);
context.writer.write_varuint32(self.name.len() as u32);
context.writer.write_utf8_string(&self.name);
}
fn fory_read_data(context: &mut ReadContext, is_field: bool) -> Result<Self, Error> {
let value = context.reader.read_i32();
let len = context.reader.read_varuint32() as usize;
let name = context.reader.read_utf8_string(len);
Ok(Self { value, name })
}
fn fory_type_id_dyn(&self, type_resolver: &TypeResolver) -> u32 {
Self::fory_get_type_id(type_resolver)
}
fn as_any(&self) -> &dyn Any {
self
}
}
impl ForyDefault for CustomType {
fn fory_default() -> Self {
Self::default()
}
}
Registering Custom Serializers
let mut fory = Fory::default();
fory.register_serializer::<CustomType>(100);
let custom = CustomType {
value: 42,
name: "test".to_string(),
};
let bytes = fory.serialize(&custom);
let decoded: CustomType = fory.deserialize(&bytes)?;
assert_eq!(custom, decoded);
WriteContext and ReadContext
The WriteContext and ReadContext provide access to:
- writer/reader: Binary buffer operations
- type_resolver: Type registration information
- ref_resolver: Reference tracking (for shared/circular references)
Common Writer Methods
// Primitive types
context.writer.write_i8(value);
context.writer.write_i16(value);
context.writer.write_i32(value);
context.writer.write_i64(value);
context.writer.write_f32(value);
context.writer.write_f64(value);
context.writer.write_bool(value);
// Variable-length integers
context.writer.write_varint32(value);
context.writer.write_varuint32(value);
// Strings
context.writer.write_utf8_string(&string);
Common Reader Methods
// Primitive types
let value = context.reader.read_i8();
let value = context.reader.read_i16();
let value = context.reader.read_i32();
let value = context.reader.read_i64();
let value = context.reader.read_f32();
let value = context.reader.read_f64();
let value = context.reader.read_bool();
// Variable-length integers
let value = context.reader.read_varint32();
let value = context.reader.read_varuint32();
// Strings
let string = context.reader.read_utf8_string(len);
Best Practices
- Use variable-length encoding for integers that may be small
- Write length first for variable-length data
- Handle errors properly in read methods
- Implement ForyDefault for schema evolution support
Related Topics
- Type Registration - Registering serializers
- Basic Serialization - Using ForyObject derive
- Schema Evolution - Compatible mode