Custom Serializers
For types that don't support #[derive(ForyStruct)], implement the Serializer trait manually.
When to Use Custom Serializers
- External types from other crates
- Types with special serialization requirements
- Existing data format compatibility
- Performance-critical custom encoding
Implementing the Serializer Trait
use fory::{Error, Fory, ForyDefault, ReadContext, Serializer, TypeResolver, WriteContext};
use std::any::Any;
#[derive(Debug, PartialEq, Default)]
struct CustomType {
value: i32,
name: String,
}
impl Serializer for CustomType {
fn fory_write_data(&self, context: &mut WriteContext) -> Result<(), Error> {
context.writer.write_i32(self.value);
context.writer.write_var_u32(self.name.len() as u32);
context.writer.write_utf8_string(&self.name);
Ok(())
}
fn fory_read_data(context: &mut ReadContext) -> Result<Self, Error> {
let value = context.reader.read_i32()?;
let len = context.reader.read_var_u32()? as usize;
let name = context.reader.read_utf8_string(len)?;
Ok(Self { value, name })
}
fn fory_type_id_dyn(&self, type_resolver: &TypeResolver) -> Result<fory::TypeId, Error> {
Self::fory_get_type_id(type_resolver)
}
fn as_any(&self) -> &dyn Any {
self
}
}
// ForyDefault delegates to Default
impl ForyDefault for CustomType {
fn fory_default() -> Self {
Self::default()
}
}
Note: When implementing
ForyDefaultmanually, ensure your type also implementsDefaultif you useSelf::default(). Alternatively, you can construct a default instance directly infory_default().Tip: If your type supports
#[derive(ForyStruct)], you can use#[fory(generate_default)]to automatically generate bothForyDefaultandDefaultimplementations.
Manual Serializers and Arc Any
If a manually registered serializer needs its type to round-trip behind
Arc<dyn Any + Send + Sync> or preserve UnknownCase payloads, implement the
send-sync Any reader and return the concrete value as a boxed Any value:
impl Serializer for CustomType {
fn fory_read_data_as_send_sync_any(
context: &mut ReadContext,
) -> Result<Box<dyn Any + Send + Sync>, Error> {
Ok(Box::new(Self::fory_read_data(context)?))
}
// Implement the ordinary Serializer methods as shown above.
// ...
}
Do not override this method for values that contain fields whose types are not
Send + Sync, such as Rc<T>, RcWeak<T>, RefCell<T>, or Cell<T>.
Registering Custom Serializers
let mut fory = Fory::builder().xlang(false).build();
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_var_i32(value);
context.writer.write_var_u32(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_var_i32();
let value = context.reader.read_var_u32();
// 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 ForyStruct derive
- Schema Evolution - Compatible mode