Merge pull request #1200 from aws-lumberyard-dev/JsonSerialization/UnsupportedWarnings

Improved reporting on unsupported types by the Json Serialization
main
AMZN-koppersr 5 years ago committed by GitHub
commit 463e0cfff3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -24,7 +24,12 @@
#include <AzCore/Serialization/Json/StringSerializer.h>
#include <AzCore/Serialization/Json/TupleSerializer.h>
#include <AzCore/Serialization/Json/UnorderedSetSerializer.h>
#include <AzCore/Serialization/Json/UnsupportedTypesSerializer.h>
#include <AzCore/Serialization/SerializeContext.h>
#include <AzCore/std/any.h>
#include <AzCore/std/optional.h>
#include <AzCore/std/tuple.h>
#include <AzCore/std/utils.h>
#include <AzCore/std/containers/array.h>
#include <AzCore/std/containers/fixed_vector.h>
#include <AzCore/std/containers/forward_list.h>
@ -33,12 +38,11 @@
#include <AzCore/std/containers/set.h>
#include <AzCore/std/containers/unordered_set.h>
#include <AzCore/std/containers/unordered_map.h>
#include <AzCore/std/containers/variant.h>
#include <AzCore/std/containers/vector.h>
#include <AzCore/std/smart_ptr/intrusive_ptr.h>
#include <AzCore/std/smart_ptr/shared_ptr.h>
#include <AzCore/std/smart_ptr/unique_ptr.h>
#include <AzCore/std/tuple.h>
#include <AzCore/std/utils.h>
namespace AZ
{
@ -98,6 +102,13 @@ namespace AZ
jsonContext->Serializer<JsonArraySerializer>()
->HandlesType<AZStd::array>();
jsonContext->Serializer<JsonAnySerializer>()
->HandlesType<AZStd::any>();
jsonContext->Serializer<JsonVariantSerializer>()
->HandlesType<AZStd::variant>();
jsonContext->Serializer<JsonOptionalSerializer>()
->HandlesType<AZStd::optional>();
MathReflect(jsonContext);
}
else if (SerializeContext* serializeContext = azrtti_cast<SerializeContext*>(reflectContext))

@ -33,34 +33,31 @@ namespace AZ
, m_serializerIter(serializerMapIter)
{}
JsonRegistrationContext::SerializerBuilder* JsonRegistrationContext::SerializerBuilder::HandlesTypeId(const Uuid& uuid)
JsonRegistrationContext::SerializerBuilder* JsonRegistrationContext::SerializerBuilder::HandlesTypeId(
const Uuid& uuid, bool overwriteExisting)
{
if (!m_context->IsRemovingReflection())
{
auto serializer = m_serializerIter->second.get();
if (uuid.IsNull())
{
AZ_Error("Serialization", false,
"Could not register Json serializer %s. Its Uuid is null.",
serializer->RTTI_GetTypeName()
);
AZ_Assert(false, "Could not register Json serializer %s. Its Uuid is null.", serializer->RTTI_GetTypeName());
return this;
}
auto serializerIter = m_context->m_handledTypesMap.find(uuid);
if (serializerIter == m_context->m_handledTypesMap.end())
if (!overwriteExisting)
{
m_context->m_handledTypesMap.emplace(uuid, serializer);
return this;
auto emplaceResult = m_context->m_handledTypesMap.try_emplace(uuid, serializer);
AZ_Assert(
emplaceResult.second,
"Couldn't register Json serializer %s. Another serializer (%s) has already been registered for the same Uuid (%s).",
serializer->RTTI_GetTypeName(), emplaceResult.first->second->RTTI_GetTypeName(),
uuid.ToString<AZStd::string>().c_str());
}
else
{
m_context->m_handledTypesMap.insert_or_assign(uuid, serializer);
}
AZ_Error("Serialization", false,
"Couldn't register Json serializer %s. Another serializer (%s) has already been registered for the same Uuid (%s).",
serializer->RTTI_GetTypeName(),
serializerIter->second->RTTI_GetTypeName(),
serializerIter->first.ToString<OSString>().c_str()
);
}
else
{

@ -63,52 +63,50 @@ namespace AZ
SerializerBuilder* operator->();
template <typename T>
SerializerBuilder* HandlesType()
SerializerBuilder* HandlesType(bool overwriteExisting = false)
{
return HandlesTypeId(azrtti_typeid<T>());
return HandlesTypeId(azrtti_typeid<T>(), overwriteExisting);
}
template <template<typename...> class T>
SerializerBuilder* HandlesType()
SerializerBuilder* HandlesType(bool overwriteExisting = false)
{
return HandlesTypeId(azrtti_typeid<T>());
return HandlesTypeId(azrtti_typeid<T>(), overwriteExisting);
}
template<template<AZStd::size_t...> class T>
SerializerBuilder* HandlesType()
SerializerBuilder* HandlesType(bool overwriteExisting = false)
{
return HandlesTypeId(azrtti_typeid<T>());
return HandlesTypeId(azrtti_typeid<T>(), overwriteExisting);
}
template<template<typename, AZStd::size_t> class T>
SerializerBuilder* HandlesType()
SerializerBuilder* HandlesType(bool overwriteExisting = false)
{
return HandlesTypeId(azrtti_typeid<T>());
return HandlesTypeId(azrtti_typeid<T>(), overwriteExisting);
}
template<template<typename, typename, AZStd::size_t> class T>
SerializerBuilder* HandlesType()
SerializerBuilder* HandlesType(bool overwriteExisting = false)
{
return HandlesTypeId(azrtti_typeid<T>());
return HandlesTypeId(azrtti_typeid<T>(), overwriteExisting);
}
template<template<typename, typename, typename, AZStd::size_t> class T>
SerializerBuilder* HandlesType()
SerializerBuilder* HandlesType(bool overwriteExisting = false)
{
return HandlesTypeId(azrtti_typeid<T>());
return HandlesTypeId(azrtti_typeid<T>(), overwriteExisting);
}
template<template<typename, AZStd::size_t, typename> class T>
SerializerBuilder* HandlesType()
SerializerBuilder* HandlesType(bool overwriteExisting = false)
{
return HandlesTypeId(azrtti_typeid<T>());
return HandlesTypeId(azrtti_typeid<T>(), overwriteExisting);
}
protected:
struct Placeholder { AZ_TYPE_INFO(PlaceHolder, "{4425191C-F497-411A-A7C3-52928E720B0A}"); };
SerializerBuilder(JsonRegistrationContext* context, SerializerMap::const_iterator serializerMapIter);
SerializerBuilder* HandlesTypeId(const AZ::Uuid& uuid);
SerializerBuilder* HandlesTypeId(const AZ::Uuid& uuid, bool overwriteExisting);
JsonRegistrationContext* m_context = nullptr;
SerializerMap::const_iterator m_serializerIter;

@ -0,0 +1,56 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include <AzCore/Memory/SystemAllocator.h>
#include <AzCore/Serialization/Json/UnsupportedTypesSerializer.h>
namespace AZ
{
AZ_CLASS_ALLOCATOR_IMPL(JsonUnsupportedTypesSerializer, SystemAllocator, 0);
AZ_CLASS_ALLOCATOR_IMPL(JsonAnySerializer, SystemAllocator, 0);
AZ_CLASS_ALLOCATOR_IMPL(JsonVariantSerializer, SystemAllocator, 0);
AZ_CLASS_ALLOCATOR_IMPL(JsonOptionalSerializer, SystemAllocator, 0);
JsonSerializationResult::Result JsonUnsupportedTypesSerializer::Load(void*, const Uuid&, const rapidjson::Value&,
JsonDeserializerContext& context)
{
namespace JSR = JsonSerializationResult;
return context.Report(JSR::Tasks::ReadField, JSR::Outcomes::Invalid, GetMessage());
}
JsonSerializationResult::Result JsonUnsupportedTypesSerializer::Store(rapidjson::Value&, const void*, const void*, const Uuid&,
JsonSerializerContext& context)
{
namespace JSR = JsonSerializationResult;
return context.Report(JSR::Tasks::WriteValue, JSR::Outcomes::Invalid, GetMessage());
}
AZStd::string_view JsonAnySerializer::GetMessage() const
{
return "The Json Serialization doesn't support AZStd::any by design. The Json Serialization attempts to minimize the use of $type, "
"in particular the guid version, but no way has yet been found to use AZStd::any without explicitly and always requiring "
"one.";
}
AZStd::string_view JsonVariantSerializer::GetMessage() const
{
return "The Json Serialization doesn't support AZStd::variant by design. The Json Serialization attempts to minimize the use of "
"$type, in particular the guid version. While combinations of AZStd::variant can be constructed that don't require a $type, "
"this cannot be guaranteed in general.";
}
AZStd::string_view JsonOptionalSerializer::GetMessage() const
{
return "The Json Serialization doesn't support AZStd::optional by design. No JSON format has yet been found that wasn't deemed too "
"complex or overly verbose.";
}
} // namespace AZ

@ -0,0 +1,72 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <AzCore/Memory/Memory.h>
#include <AzCore/Serialization/Json/BaseJsonSerializer.h>
#include <AzCore/std/string/string_view.h>
namespace AZ
{
class JsonUnsupportedTypesSerializer : public BaseJsonSerializer
{
public:
AZ_RTTI(JsonUnsupportedTypesSerializer, "{AFCC76B9-1F28-429D-8B4E-020BFD95ADAC}", BaseJsonSerializer);
AZ_CLASS_ALLOCATOR_DECL;
JsonSerializationResult::Result Load(
void* outputValue,
const Uuid& outputValueTypeId,
const rapidjson::Value& inputValue,
JsonDeserializerContext& context) override;
JsonSerializationResult::Result Store(
rapidjson::Value& outputValue,
const void* inputValue,
const void* defaultValue,
const Uuid& valueTypeId,
JsonSerializerContext& context) override;
protected:
virtual AZStd::string_view GetMessage() const = 0;
};
class JsonAnySerializer : public JsonUnsupportedTypesSerializer
{
public:
AZ_RTTI(JsonAnySerializer, "{699A0864-C4E2-4266-8141-99793C76870F}", JsonUnsupportedTypesSerializer);
AZ_CLASS_ALLOCATOR_DECL;
protected:
AZStd::string_view GetMessage() const override;
};
class JsonVariantSerializer : public JsonUnsupportedTypesSerializer
{
public:
AZ_RTTI(JsonVariantSerializer, "{08F8E746-F8A4-4E83-8902-713E90F3F498}", JsonUnsupportedTypesSerializer);
AZ_CLASS_ALLOCATOR_DECL;
protected:
AZStd::string_view GetMessage() const override;
};
class JsonOptionalSerializer : public JsonUnsupportedTypesSerializer
{
public:
AZ_RTTI(JsonOptionalSerializer, "{F8AF1C95-BD1B-44D2-9B4A-F5726133A104}", JsonUnsupportedTypesSerializer);
AZ_CLASS_ALLOCATOR_DECL;
protected:
AZStd::string_view GetMessage() const override;
};
} // namespace AZ

@ -544,6 +544,8 @@ set(FILES
Serialization/Json/TupleSerializer.cpp
Serialization/Json/UnorderedSetSerializer.h
Serialization/Json/UnorderedSetSerializer.cpp
Serialization/Json/UnsupportedTypesSerializer.h
Serialization/Json/UnsupportedTypesSerializer.cpp
Serialization/std/VariantReflection.inl
Settings/CommandLine.cpp
Settings/CommandLine.h

@ -95,6 +95,20 @@ namespace JsonSerializationTests
}
};
class SerializerWithOneDuplicatedTypeWithOverride
: public JsonSerializerTemplatedMock<SerializerWithOneDuplicatedTypeWithOverride>
{
public:
AZ_RTTI(SerializerWithOneDuplicatedTypeWithOverride, "{4218D591-E578-499B-B578-ACA70C9944AB}", BaseJsonSerializer);
~SerializerWithOneDuplicatedTypeWithOverride() override = default;
static void Reflect(AZ::JsonRegistrationContext* context)
{
context->Serializer<SerializerWithOneDuplicatedTypeWithOverride>()
->HandlesType<bool>(true);
}
};
// Attempts to register the same type twice
class SerializerWithTwoSameTypes
: public JsonSerializerTemplatedMock<SerializerWithTwoSameTypes>
@ -271,13 +285,29 @@ namespace JsonSerializationTests
EXPECT_EQ(1, m_jsonRegistrationContext->GetRegisteredSerializers().size());
AZ::BaseJsonSerializer* mockSerializer = m_jsonRegistrationContext->GetSerializerForType(azrtti_typeid<bool>());
EXPECT_NE(mockSerializer, nullptr);
ASSERT_NE(mockSerializer, nullptr);
EXPECT_EQ(AZ::AzTypeInfo<SerializerWithOneType>::Uuid(), mockSerializer->RTTI_GetType());
SerializerWithOneType::Unreflect(m_jsonRegistrationContext.get());
SerializerWithOneDuplicatedType::Unreflect(m_jsonRegistrationContext.get());
}
TEST_F(JsonRegistrationContextTests, OverwriteRegisterSameUuidWithMultipleSerializers_Succeeds)
{
EXPECT_NE(AZ::AzTypeInfo<SerializerWithOneDuplicatedTypeWithOverride>::Uuid(), AZ::AzTypeInfo<SerializerWithOneType>::Uuid());
SerializerWithOneType::Reflect(m_jsonRegistrationContext.get());
SerializerWithOneDuplicatedTypeWithOverride::Reflect(m_jsonRegistrationContext.get());
EXPECT_EQ(1, m_jsonRegistrationContext->GetRegisteredSerializers().size());
AZ::BaseJsonSerializer* mockSerializer = m_jsonRegistrationContext->GetSerializerForType(azrtti_typeid<bool>());
ASSERT_NE(mockSerializer, nullptr);
EXPECT_EQ(AZ::AzTypeInfo<SerializerWithOneDuplicatedTypeWithOverride>::Uuid(), mockSerializer->RTTI_GetType());
SerializerWithOneType::Unreflect(m_jsonRegistrationContext.get());
SerializerWithOneDuplicatedTypeWithOverride::Unreflect(m_jsonRegistrationContext.get());
}
TEST_F(JsonRegistrationContextTests, RegisterSameUuidWithSameSerializers_Fails)
{
AZ_TEST_START_ASSERTTEST;

@ -0,0 +1,142 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include <AzCore/Serialization/Json/UnsupportedTypesSerializer.h>
#include <AzCore/std/any.h>
#include <AzCore/std/optional.h>
#include <AzCore/std/containers/variant.h>
#include <Tests/Serialization/Json/BaseJsonSerializerFixture.h>
namespace JsonSerializationTests
{
struct AnyInfo
{
using Type = AZStd::any;
using Serializer = AZ::JsonAnySerializer;
};
struct VariantInfo
{
using Type = AZStd::variant<AZStd::monostate, int, double>;
using Serializer = AZ::JsonVariantSerializer;
};
struct OptionalInfo
{
using Type = AZStd::optional<int>;
using Serializer = AZ::JsonVariantSerializer;
};
template<typename Info>
class JsonUnsupportedTypesSerializerTests : public BaseJsonSerializerFixture
{
public:
using Type = typename Info::Type;
using Serializer = typename Info::Serializer;
void SetUp() override
{
BaseJsonSerializerFixture::SetUp();
this->m_serializer = AZStd::make_unique<Serializer>();
}
void TearDown() override
{
this->m_serializer.reset();
BaseJsonSerializerFixture::TearDown();
}
protected:
AZStd::unique_ptr<Serializer> m_serializer;
Type m_instance{};
};
using UnsupportedTypesTestTypes = ::testing::Types<AnyInfo, VariantInfo, OptionalInfo>;
TYPED_TEST_CASE(JsonUnsupportedTypesSerializerTests, UnsupportedTypesTestTypes);
TYPED_TEST(JsonUnsupportedTypesSerializerTests, Load_CallDirectly_ReportsIssueAndHalts)
{
namespace JSR = AZ::JsonSerializationResult;
bool hasMessage = false;
auto callback = [&hasMessage](AZStd::string_view message, JSR::ResultCode result, AZStd::string_view) -> JSR::ResultCode
{
hasMessage = !message.empty();
return result;
};
this->m_jsonDeserializationContext->PushReporter(AZStd::move(callback));
JSR::Result result = this->m_serializer->Load(
&this->m_instance, azrtti_typeid(this->m_instance), *this->m_jsonDocument, *this->m_jsonDeserializationContext);
this->m_jsonDeserializationContext->PopReporter();
EXPECT_EQ(JSR::Processing::Halted, result.GetResultCode().GetProcessing());
EXPECT_TRUE(hasMessage);
}
TYPED_TEST(JsonUnsupportedTypesSerializerTests, Load_CallThroughFrontEnd_ReportsIssueAndHalts)
{
namespace JSR = AZ::JsonSerializationResult;
bool hasMessage = false;
auto callback = [&hasMessage](AZStd::string_view message, JSR::ResultCode result, AZStd::string_view) -> JSR::ResultCode
{
hasMessage = !message.empty();
return result;
};
this->m_deserializationSettings->m_reporting = AZStd::move(callback);
JSR::ResultCode result = AZ::JsonSerialization::Load(this->m_instance, *this->m_jsonDocument, *this->m_deserializationSettings);
EXPECT_EQ(JSR::Processing::Halted, result.GetProcessing());
EXPECT_TRUE(hasMessage);
}
TYPED_TEST(JsonUnsupportedTypesSerializerTests, Save_CallDirectly_ReportsIssueAndHalts)
{
namespace JSR = AZ::JsonSerializationResult;
bool hasMessage = false;
auto callback = [&hasMessage](AZStd::string_view message, JSR::ResultCode result, AZStd::string_view) -> JSR::ResultCode
{
hasMessage = !message.empty();
return result;
};
this->m_jsonSerializationContext->PushReporter(AZStd::move(callback));
JSR::Result result = this->m_serializer->Store(
*this->m_jsonDocument, &this->m_instance, nullptr, azrtti_typeid(this->m_instance), *this->m_jsonSerializationContext);
this->m_jsonSerializationContext->PopReporter();
EXPECT_EQ(JSR::Processing::Halted, result.GetResultCode().GetProcessing());
EXPECT_TRUE(hasMessage);
}
TYPED_TEST(JsonUnsupportedTypesSerializerTests, Save_CallThroughFrontEnd_ReportsIssueAndHalts)
{
namespace JSR = AZ::JsonSerializationResult;
bool hasMessage = false;
auto callback = [&hasMessage](AZStd::string_view message, JSR::ResultCode result, AZStd::string_view) -> JSR::ResultCode
{
hasMessage = !message.empty();
return result;
};
this->m_serializationSettings->m_reporting = AZStd::move(callback);
JSR::ResultCode result = AZ::JsonSerialization::Store(
*this->m_jsonDocument, this->m_jsonDocument->GetAllocator(), this->m_instance, *this->m_serializationSettings);
EXPECT_EQ(JSR::Processing::Halted, result.GetProcessing());
EXPECT_TRUE(hasMessage);
}
} // namespace JsonSerializationTests

@ -128,6 +128,7 @@ set(FILES
Serialization/Json/TransformSerializerTests.cpp
Serialization/Json/TupleSerializerTests.cpp
Serialization/Json/UnorderedSetSerializerTests.cpp
Serialization/Json/UnsupportedTypesSerializerTests.cpp
Serialization/Json/UuidSerializerTests.cpp
Math/AabbTests.cpp
Math/ColorTests.cpp

Loading…
Cancel
Save