From 01b910a1480cb0b8094c87230b1ad91a0598ddd6 Mon Sep 17 00:00:00 2001 From: Gino Valente <49806985+MrGVSV@users.noreply.github.com> Date: Mon, 16 Oct 2023 16:31:16 -0700 Subject: [PATCH] bevy_reflect: Fix dynamic type serialization (#10103) # Objective Fixes #10086 ## Solution Instead of serializing via `DynamicTypePath::reflect_type_path`, now uses the `TypePath` found on the `TypeInfo` returned by `Reflect::get_represented_type_info`. This issue was happening because the dynamic types implement `TypePath` themselves and do not (and cannot) forward their proxy's `TypePath` data. The solution was to access the proxy's type information in order to get the correct `TypePath` data. ## Changed - The `Debug` impl for `TypePathTable` now includes output for all fields. --- crates/bevy_reflect/src/serde/mod.rs | 40 +++++++++++++++++++++++++--- crates/bevy_reflect/src/serde/ser.rs | 17 +++++++++++- crates/bevy_reflect/src/type_path.rs | 4 +++ 3 files changed, 57 insertions(+), 4 deletions(-) diff --git a/crates/bevy_reflect/src/serde/mod.rs b/crates/bevy_reflect/src/serde/mod.rs index b675b5dcb2..5f87eba8f3 100644 --- a/crates/bevy_reflect/src/serde/mod.rs +++ b/crates/bevy_reflect/src/serde/mod.rs @@ -8,7 +8,7 @@ pub use type_data::*; #[cfg(test)] mod tests { - use crate::{self as bevy_reflect, DynamicTupleStruct}; + use crate::{self as bevy_reflect, DynamicTupleStruct, Struct}; use crate::{ serde::{ReflectSerializer, UntypedReflectDeserializer}, type_registry::TypeRegistry, @@ -94,8 +94,10 @@ mod tests { } #[test] - #[should_panic(expected = "cannot get type info for bevy_reflect::DynamicStruct")] - fn unproxied_dynamic_should_not_serialize() { + #[should_panic( + expected = "cannot serialize dynamic value without represented type: bevy_reflect::DynamicStruct" + )] + fn should_not_serialize_unproxied_dynamic() { let registry = TypeRegistry::default(); let mut value = DynamicStruct::default(); @@ -104,4 +106,36 @@ mod tests { let serializer = ReflectSerializer::new(&value, ®istry); ron::ser::to_string(&serializer).unwrap(); } + + #[test] + fn should_roundtrip_proxied_dynamic() { + #[derive(Reflect)] + struct TestStruct { + a: i32, + b: i32, + } + + let mut registry = TypeRegistry::default(); + registry.register::(); + + let value: DynamicStruct = TestStruct { a: 123, b: 456 }.clone_dynamic(); + + let serializer = ReflectSerializer::new(&value, ®istry); + + let expected = r#"{"bevy_reflect::serde::tests::TestStruct":(a:123,b:456)}"#; + let result = ron::ser::to_string(&serializer).unwrap(); + assert_eq!(expected, result); + + let mut deserializer = ron::de::Deserializer::from_str(&result).unwrap(); + let reflect_deserializer = UntypedReflectDeserializer::new(®istry); + + let expected = value.clone_value(); + let result = reflect_deserializer + .deserialize(&mut deserializer) + .unwrap() + .take::() + .unwrap(); + + assert!(expected.reflect_partial_eq(&result).unwrap()); + } } diff --git a/crates/bevy_reflect/src/serde/ser.rs b/crates/bevy_reflect/src/serde/ser.rs index da0b464893..79ec73099a 100644 --- a/crates/bevy_reflect/src/serde/ser.rs +++ b/crates/bevy_reflect/src/serde/ser.rs @@ -68,7 +68,22 @@ impl<'a> Serialize for ReflectSerializer<'a> { { let mut state = serializer.serialize_map(Some(1))?; state.serialize_entry( - self.value.reflect_type_path(), + self.value + .get_represented_type_info() + .ok_or_else(|| { + if self.value.is_dynamic() { + Error::custom(format_args!( + "cannot serialize dynamic value without represented type: {}", + self.value.reflect_type_path() + )) + } else { + Error::custom(format_args!( + "cannot get type info for {}", + self.value.reflect_type_path() + )) + } + })? + .type_path(), &TypedReflectSerializer::new(self.value, self.registry), )?; state.end() diff --git a/crates/bevy_reflect/src/type_path.rs b/crates/bevy_reflect/src/type_path.rs index 9861f76289..99f9b81e6e 100644 --- a/crates/bevy_reflect/src/type_path.rs +++ b/crates/bevy_reflect/src/type_path.rs @@ -183,6 +183,10 @@ impl fmt::Debug for TypePathTable { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("TypePathVtable") .field("type_path", &self.type_path) + .field("short_type_path", &(self.short_type_path)()) + .field("type_ident", &(self.type_ident)()) + .field("crate_name", &(self.crate_name)()) + .field("module_path", &(self.module_path)()) .finish() } }