Support deserializing non-reflected enums (#815)
The serialize context allows users to reflect fields that are enums to a class without reflecting the enum type itself with the EnumBuilder. In this case, the serialize context stores the mapping of the enum's typeid to the underlying type's typeid. When asking for the class data for the enum typeid, the underlying type's class data is returned. This was throwing off the json serializer, which would then see that the type was "unsigned int" instead of an enum, and attempt to load the unsigned int value. The unsigned int deserializer would then complain, because the incoming typeid was the typeid of the enum, and not equal to the typeid of unsigned int. This change adds support for detecting the non-reflected enum, and loading it properly.
This commit is contained in:
@@ -10,6 +10,7 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#include "AzCore/RTTI/TypeInfo.h"
|
||||
#include <AzCore/Math/UuidSerializer.h>
|
||||
#include <AzCore/RTTI/AttributeReader.h>
|
||||
#include <AzCore/Serialization/Json/CastingHelpers.h>
|
||||
@@ -61,6 +62,13 @@ namespace AZ
|
||||
|
||||
if (classData->m_azRtti && classData->m_azRtti->GetGenericTypeId() != typeId)
|
||||
{
|
||||
if (((classData->m_azRtti->GetTypeTraits() & (AZ::TypeTraits::is_signed | AZ::TypeTraits::is_unsigned)) != AZ::TypeTraits{0}) &&
|
||||
context.GetSerializeContext()->GetUnderlyingTypeId(typeId) == classData->m_typeId)
|
||||
{
|
||||
// This value is from an enum, where a field has been reflected using ClassBuilder::Field, but the enum
|
||||
// type itself has not been reflected using EnumBuilder. Treat it as an enum.
|
||||
return LoadEnum(object, *classData, value, context);
|
||||
}
|
||||
serializer = context.GetRegistrationContext()->GetSerializerForType(classData->m_azRtti->GetGenericTypeId());
|
||||
if (serializer)
|
||||
{
|
||||
@@ -77,21 +85,18 @@ namespace AZ
|
||||
{
|
||||
return LoadEnum(object, *classData, value, context);
|
||||
}
|
||||
else if (classData->m_container)
|
||||
if (classData->m_container)
|
||||
{
|
||||
return context.Report(Tasks::ReadField, Outcomes::Unsupported,
|
||||
"The Json Serializer uses custom serializers to load containers. If this message is encountered "
|
||||
"then a serializer for the target containers is missing, isn't registered or doesn't exist.");
|
||||
}
|
||||
else if (value.IsObject())
|
||||
if (value.IsObject())
|
||||
{
|
||||
return LoadClass(object, *classData, value, context);
|
||||
}
|
||||
else
|
||||
{
|
||||
return context.Report(Tasks::ReadField, Outcomes::Unsupported,
|
||||
AZStd::string::format("Reading into targets of type '%s' is not supported.", classData->m_name));
|
||||
}
|
||||
return context.Report(Tasks::ReadField, Outcomes::Unsupported,
|
||||
AZStd::string::format("Reading into targets of type '%s' is not supported.", classData->m_name));
|
||||
}
|
||||
|
||||
JsonSerializationResult::ResultCode JsonDeserializer::LoadToPointer(void* object, const Uuid& typeId,
|
||||
@@ -233,8 +238,16 @@ namespace AZ
|
||||
AZ::TypeId underlyingTypeId = AZ::TypeId::CreateNull();
|
||||
if (!attributeReader.Read<AZ::TypeId>(underlyingTypeId))
|
||||
{
|
||||
return context.Report(Tasks::RetrieveInfo, Outcomes::Unknown,
|
||||
"Unable to find underlying type of enum in class data.");
|
||||
// for non-reflected enums, the passed-in classData already represents the enum's underlying type
|
||||
if (context.GetSerializeContext()->GetUnderlyingTypeId(classData.m_typeId) == classData.m_typeId)
|
||||
{
|
||||
underlyingTypeId = classData.m_typeId;
|
||||
}
|
||||
else
|
||||
{
|
||||
return context.Report(Tasks::RetrieveInfo, Outcomes::Unknown,
|
||||
"Unable to find underlying type of enum in class data.");
|
||||
}
|
||||
}
|
||||
|
||||
const SerializeContext::ClassData* underlyingClassData = context.GetSerializeContext()->FindClassData(underlyingTypeId);
|
||||
|
||||
@@ -21,7 +21,7 @@ namespace JsonSerializationTests
|
||||
{
|
||||
using JsonSerializationTestCases = ::testing::Types<
|
||||
// Structures
|
||||
SimpleClass, SimpleInheritence, MultipleInheritence, SimpleNested, SimpleEnumWrapper,
|
||||
SimpleClass, SimpleInheritence, MultipleInheritence, SimpleNested, SimpleEnumWrapper, NonReflectedEnumWrapper,
|
||||
// Pointers
|
||||
SimpleNullPointer, SimpleAssignedPointer, ComplexAssignedPointer, ComplexNullInheritedPointer,
|
||||
ComplexAssignedDifferentInheritedPointer, ComplexAssignedSameInheritedPointer,
|
||||
|
||||
@@ -373,6 +373,57 @@ namespace JsonSerializationTests
|
||||
return MakeInstanceWithoutDefaults(AZStd::move(instance), json);
|
||||
}
|
||||
|
||||
// NonReflectedEnumWrapper
|
||||
bool NonReflectedEnumWrapper::Equals(const NonReflectedEnumWrapper& rhs, bool fullReflection) const
|
||||
{
|
||||
return !fullReflection || (m_enumClass == rhs.m_enumClass && m_rawEnum== rhs.m_rawEnum);
|
||||
}
|
||||
|
||||
void NonReflectedEnumWrapper::Reflect(AZStd::unique_ptr<AZ::SerializeContext>& context, bool fullReflection)
|
||||
{
|
||||
if (fullReflection)
|
||||
{
|
||||
// Note that the enums are not reflected using context->Enum<>
|
||||
|
||||
context->Class<NonReflectedEnumWrapper>()
|
||||
->Field("enumClass", &NonReflectedEnumWrapper::m_enumClass)
|
||||
->Field("rawEnum", &NonReflectedEnumWrapper::m_rawEnum);
|
||||
}
|
||||
}
|
||||
|
||||
InstanceWithSomeDefaults<NonReflectedEnumWrapper> NonReflectedEnumWrapper::GetInstanceWithSomeDefaults()
|
||||
{
|
||||
auto instance = AZStd::make_unique<NonReflectedEnumWrapper>();
|
||||
instance->m_enumClass = NonReflectedEnumWrapper::SimpleEnumClass::Option2;
|
||||
|
||||
const char* strippedDefaults = R"(
|
||||
{
|
||||
"enumClass": 2
|
||||
})";
|
||||
const char* keptDefaults = R"(
|
||||
{
|
||||
"enumClass": 2,
|
||||
"rawEnum": 0
|
||||
})";
|
||||
|
||||
return MakeInstanceWithSomeDefaults(AZStd::move(instance),
|
||||
strippedDefaults, keptDefaults);
|
||||
}
|
||||
|
||||
InstanceWithoutDefaults<NonReflectedEnumWrapper> NonReflectedEnumWrapper::GetInstanceWithoutDefaults()
|
||||
{
|
||||
auto instance = AZStd::make_unique<NonReflectedEnumWrapper>();
|
||||
instance->m_enumClass = NonReflectedEnumWrapper::SimpleEnumClass::Option2;
|
||||
instance->m_rawEnum = NonReflectedEnumWrapper::SimpleRawEnum::RawOption1;
|
||||
|
||||
const char* json = R"(
|
||||
{
|
||||
"enumClass": 2,
|
||||
"rawEnum": 1
|
||||
})";
|
||||
return MakeInstanceWithoutDefaults(AZStd::move(instance), json);
|
||||
}
|
||||
|
||||
// TemplatedClass<int>
|
||||
|
||||
bool TemplatedClass<int>::Equals(const TemplatedClass<int>& rhs, bool fullReflection) const
|
||||
|
||||
@@ -134,6 +134,35 @@ namespace JsonSerializationTests
|
||||
SimpleRawEnum m_rawEnum{};
|
||||
};
|
||||
|
||||
struct NonReflectedEnumWrapper
|
||||
{
|
||||
enum class SimpleEnumClass
|
||||
{
|
||||
Option1 = 1,
|
||||
Option2,
|
||||
};
|
||||
enum SimpleRawEnum
|
||||
{
|
||||
RawOption1 = 1,
|
||||
RawOption2,
|
||||
};
|
||||
AZ_CLASS_ALLOCATOR(NonReflectedEnumWrapper, AZ::SystemAllocator, 0);
|
||||
AZ_RTTI(NonReflectedEnumWrapper, "{A80D5B6B-2FD1-46E9-A7A9-44C5E2650526}");
|
||||
|
||||
static constexpr bool SupportsPartialDefaults = true;
|
||||
|
||||
NonReflectedEnumWrapper() = default;
|
||||
virtual ~NonReflectedEnumWrapper() = default;
|
||||
|
||||
bool Equals(const NonReflectedEnumWrapper& rhs, bool fullReflection) const;
|
||||
static void Reflect(AZStd::unique_ptr<AZ::SerializeContext>& context, bool fullReflection);
|
||||
static InstanceWithSomeDefaults<NonReflectedEnumWrapper> GetInstanceWithSomeDefaults();
|
||||
static InstanceWithoutDefaults<NonReflectedEnumWrapper> GetInstanceWithoutDefaults();
|
||||
|
||||
SimpleEnumClass m_enumClass{};
|
||||
SimpleRawEnum m_rawEnum{};
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct TemplatedClass
|
||||
{
|
||||
@@ -158,5 +187,7 @@ namespace AZ
|
||||
{
|
||||
AZ_TYPE_INFO_SPECIALIZE(JsonSerializationTests::SimpleEnumWrapper::SimpleEnumClass, "{AF6F1964-5B20-4689-BF23-F36B9C9AAE6A}");
|
||||
AZ_TYPE_INFO_SPECIALIZE(JsonSerializationTests::SimpleEnumWrapper::SimpleRawEnum, "{EB24207F-B48F-4D8B-940D-3CD06A371739}");
|
||||
AZ_TYPE_INFO_SPECIALIZE(JsonSerializationTests::NonReflectedEnumWrapper::SimpleEnumClass, "{E80E4A41-B29E-4B7C-B630-3B599172C837}");
|
||||
AZ_TYPE_INFO_SPECIALIZE(JsonSerializationTests::NonReflectedEnumWrapper::SimpleRawEnum, "{C42AF28D-4F84-4540-972A-5B6EEFAB13FF}");
|
||||
AZ_TYPE_INFO_TEMPLATE(JsonSerializationTests::TemplatedClass, "{CA4ADF74-66E7-4D16-B4AC-F71278C60EC7}", AZ_TYPE_INFO_TYPENAME);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user