Skip to main content
Version: dev

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 ForyDefault manually, ensure your type also implements Default if you use Self::default(). Alternatively, you can construct a default instance directly in fory_default().

Tip: If your type supports #[derive(ForyStruct)], you can use #[fory(generate_default)] to automatically generate both ForyDefault and Default implementations.

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

  1. Use variable-length encoding for integers that may be small
  2. Write length first for variable-length data
  3. Handle errors properly in read methods
  4. Implement ForyDefault for schema evolution support