title: Custom Serializers sidebar_position: 10 id: custom_serializers license: | Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file distributed with this work for additional information regarding copyright ownership. The ASF licenses this file to You under the Apache License, Version 2.0 (the “License”); you may not use this file except in compliance with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
For types that don't support #[derive(ForyStruct)], implement the Serializer trait manually.
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.
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>.
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);
The WriteContext and ReadContext provide access to:
// 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);
// 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);