bevy_reflect::serde

Trait ReflectSerializerProcessor

source
pub trait ReflectSerializerProcessor {
    // Required method
    fn try_serialize<S>(
        &self,
        value: &dyn PartialReflect,
        registry: &TypeRegistry,
        serializer: S,
    ) -> Result<Result<S::Ok, S>, S::Error>
       where S: Serializer;
}
Expand description

Allows overriding the default serialization behavior of ReflectSerializer and TypedReflectSerializer for specific values.

When serializing a reflected value, you may want to override the default behavior and use your own logic for serialization. This logic may also be context-dependent, and only apply for a single use of your ReflectSerializer. To achieve this, you can create a processor and pass it into your serializer.

Whenever the serializer attempts to serialize a value, it will first call try_serialize on your processor, which may take ownership of the serializer and write into the serializer (successfully or not), or return ownership of the serializer back, and continue with the default logic.

The deserialization equivalent of this is ReflectDeserializerProcessor.

§Compared to SerializeWithRegistry

SerializeWithRegistry allows you to define how your type will be serialized by a TypedReflectSerializer, given the extra context of the TypeRegistry. If your type can be serialized entirely using that, then you should prefer implementing that trait instead of using a processor.

However, you may need more context-dependent data which is only present in the scope where you create the TypedReflectSerializer. For example, if you need to use a reference to a value while serializing, then there is no way to do this with SerializeWithRegistry as you can’t pass that reference into anywhere. This is where a processor is useful, as the processor can capture local variables.

A ReflectSerializerProcessor always takes priority over a SerializeWithRegistry implementation, so this is also useful for overriding serialization behavior if you need to do something custom.

§Examples

Serializing a reflected value when saving an asset to disk, and replacing asset handles with the handle path (if it has one):

#[derive(Debug, Clone, Reflect)]
struct MyAsset {
    name: String,
    mesh: Handle<Mesh>,
}

struct HandleProcessor;

impl ReflectSerializerProcessor for HandleProcessor {
    fn try_serialize<S>(
        &self,
        value: &dyn PartialReflect,
        registry: &TypeRegistry,
        serializer: S,
    ) -> Result<Result<S::Ok, S>, S::Error>
    where
        S: serde::Serializer,
    {
        let Some(value) = value.try_as_reflect() else {
            // we don't have any info on this type; do the default serialization logic
            return Ok(Err(serializer));
        };
        let type_id = value.reflect_type_info().type_id();
        let Some(reflect_handle) = registry.get_type_data::<ReflectHandle>(type_id) else {
            // this isn't a `Handle<T>`
            return Ok(Err(serializer));
        };

        let untyped_handle = reflect_handle
            .downcast_handle_untyped(value.as_any())
            .unwrap();
        if let Some(path) = untyped_handle.path() {
            Ok(Ok(serializer.serialize_str(path)?))
        } else {
            Ok(Ok(serializer.serialize_unit()?))
        }
    }
}

fn save(type_registry: &TypeRegistry, asset: &MyAsset) -> Result<Vec<u8>, AssetError> {
    let mut asset_bytes = Vec::new();

    let processor = HandleProcessor;
    let serializer = ReflectSerializer::with_processor(asset, type_registry, &processor);
    let mut ron_serializer = ron::Serializer::new(&mut asset_bytes, None)?;

    serializer.serialize(&mut ron_serializer)?;
    Ok(asset_bytes)
}

Required Methods§

source

fn try_serialize<S>( &self, value: &dyn PartialReflect, registry: &TypeRegistry, serializer: S, ) -> Result<Result<S::Ok, S>, S::Error>
where S: Serializer,

Attempts to serialize the value which a TypedReflectSerializer is currently looking at.

If you want to override the default serialization, return Ok(Ok(value)) with an Ok output from the serializer.

If you don’t want to override the serialization, return ownership of the serializer back via Ok(Err(serializer)).

You can use the type registry to read info about the type you’re serializing, or just try to downcast the value directly:

struct I32AsStringProcessor;

impl ReflectSerializerProcessor for I32AsStringProcessor {
    fn try_serialize<S>(
        &self,
        value: &dyn PartialReflect,
        registry: &TypeRegistry,
        serializer: S,
    ) -> Result<Result<S::Ok, S>, S::Error>
    where
        S: serde::Serializer
    {
        if let Some(value) = value.try_downcast_ref::<i32>() {
            let value_as_string = format!("{value:?}");
            Ok(Ok(serializer.serialize_str(&value_as_string)?))
        } else {
            // Not an `i32`, just do the default serialization
            Ok(Err(serializer))
        }
    }
}

Object Safety§

This trait is not object safe.

Implementations on Foreign Types§

source§

impl ReflectSerializerProcessor for ()

source§

fn try_serialize<S>( &self, _value: &dyn PartialReflect, _registry: &TypeRegistry, serializer: S, ) -> Result<Result<S::Ok, S>, S::Error>
where S: Serializer,

Implementors§