Merge branch 'development' into cmake/linux_fix_warn_unused
Signed-off-by: Esteban Papp <81431996+amznestebanpapp@users.noreply.github.com>
This commit is contained in:
@@ -81,9 +81,14 @@ namespace AZ
|
||||
azrtti_typeid<decltype(componentMap)>(),
|
||||
inputValue, "Components", context);
|
||||
|
||||
static TypeId genericComponentWrapperTypeId("{68D358CA-89B9-4730-8BA6-E181DEA28FDE}");
|
||||
for (auto& [componentKey, component] : componentMap)
|
||||
{
|
||||
entityInstance->m_components.emplace_back(component);
|
||||
// if underlying type is genericComponentWrapperTypeId, the template is null and the component should not be addded
|
||||
if (component->GetUnderlyingComponentType() != genericComponentWrapperTypeId)
|
||||
{
|
||||
entityInstance->m_components.emplace_back(component);
|
||||
}
|
||||
}
|
||||
|
||||
result.Combine(componentLoadResult);
|
||||
|
||||
@@ -209,39 +209,35 @@ namespace AZ
|
||||
//! performance sensitive code.
|
||||
AZ_INLINE bool CompareAnyValue(const AZStd::any& lhs, const AZStd::any& rhs)
|
||||
{
|
||||
bool isEqual = false;
|
||||
|
||||
if (lhs.type() != rhs.type())
|
||||
if (lhs.type() == rhs.type())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
AZ::SerializeContext* serializeContext = nullptr;
|
||||
AZ::ComponentApplicationBus::BroadcastResult(serializeContext, &AZ::ComponentApplicationRequests::GetSerializeContext);
|
||||
|
||||
AZ::SerializeContext* serializeContext = nullptr;
|
||||
AZ::ComponentApplicationBus::BroadcastResult(serializeContext, &AZ::ComponentApplicationRequests::GetSerializeContext);
|
||||
|
||||
const AZ::SerializeContext::ClassData* classData = serializeContext->FindClassData(lhs.type());
|
||||
if (classData)
|
||||
{
|
||||
if (classData->m_serializer)
|
||||
const AZ::SerializeContext::ClassData* classData = serializeContext->FindClassData(lhs.type());
|
||||
if (classData)
|
||||
{
|
||||
isEqual = classData->m_serializer->CompareValueData(AZStd::any_cast<void>(&lhs), AZStd::any_cast<void>(&rhs));
|
||||
}
|
||||
else
|
||||
{
|
||||
AZStd::vector<AZ::u8> myData;
|
||||
AZ::IO::ByteContainerStream<decltype(myData)> myDataStream(&myData);
|
||||
if (classData->m_serializer)
|
||||
{
|
||||
return classData->m_serializer->CompareValueData(AZStd::any_cast<void>(&lhs), AZStd::any_cast<void>(&rhs));
|
||||
}
|
||||
else
|
||||
{
|
||||
AZStd::vector<AZ::u8> myData;
|
||||
AZ::IO::ByteContainerStream<decltype(myData)> myDataStream(&myData);
|
||||
|
||||
AZ::Utils::SaveObjectToStream(myDataStream, AZ::ObjectStream::ST_BINARY, AZStd::any_cast<void>(&lhs), lhs.type());
|
||||
AZ::Utils::SaveObjectToStream(myDataStream, AZ::ObjectStream::ST_BINARY, AZStd::any_cast<void>(&lhs), lhs.type());
|
||||
|
||||
AZStd::vector<AZ::u8> otherData;
|
||||
AZ::IO::ByteContainerStream<decltype(otherData)> otherDataStream(&otherData);
|
||||
AZStd::vector<AZ::u8> otherData;
|
||||
AZ::IO::ByteContainerStream<decltype(otherData)> otherDataStream(&otherData);
|
||||
|
||||
AZ::Utils::SaveObjectToStream(otherDataStream, AZ::ObjectStream::ST_BINARY, AZStd::any_cast<void>(&rhs), rhs.type());
|
||||
isEqual = (myData.size() == otherData.size()) && (memcmp(myData.data(), otherData.data(), myData.size()) == 0);
|
||||
AZ::Utils::SaveObjectToStream(otherDataStream, AZ::ObjectStream::ST_BINARY, AZStd::any_cast<void>(&rhs), rhs.type());
|
||||
return (myData.size() == otherData.size()) && (memcmp(myData.data(), otherData.data(), myData.size()) == 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return isEqual;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -213,9 +213,13 @@ namespace AZ
|
||||
void* object, const Uuid& typeId, const rapidjson::Value& value, JsonDeserializerContext& context, ContinuationFlags flags)
|
||||
{
|
||||
bool loadAsNewInstance = (flags & ContinuationFlags::LoadAsNewInstance) == ContinuationFlags::LoadAsNewInstance;
|
||||
JsonDeserializer::UseTypeDeserializer useCustom = (flags & ContinuationFlags::IgnoreTypeSerializer) == ContinuationFlags::IgnoreTypeSerializer
|
||||
? JsonDeserializer::UseTypeDeserializer::No
|
||||
: JsonDeserializer::UseTypeDeserializer::Yes;
|
||||
|
||||
return (flags & ContinuationFlags::ResolvePointer) == ContinuationFlags::ResolvePointer
|
||||
? JsonDeserializer::LoadToPointer(object, typeId, value, context)
|
||||
: JsonDeserializer::Load(object, typeId, value, loadAsNewInstance, context);
|
||||
? JsonDeserializer::LoadToPointer(object, typeId, value, useCustom, context)
|
||||
: JsonDeserializer::Load(object, typeId, value, loadAsNewInstance, useCustom, context);
|
||||
}
|
||||
|
||||
JsonSerializationResult::ResultCode BaseJsonSerializer::ContinueStoring(
|
||||
@@ -224,11 +228,15 @@ namespace AZ
|
||||
{
|
||||
using namespace JsonSerializationResult;
|
||||
|
||||
JsonSerializer::UseTypeSerializer useCustom = (flags & ContinuationFlags::IgnoreTypeSerializer) == ContinuationFlags::IgnoreTypeSerializer
|
||||
? JsonSerializer::UseTypeSerializer::No
|
||||
: JsonSerializer::UseTypeSerializer::Yes;
|
||||
|
||||
if ((flags & ContinuationFlags::ReplaceDefault) == ContinuationFlags::ReplaceDefault && !context.ShouldKeepDefaults())
|
||||
{
|
||||
if ((flags & ContinuationFlags::ResolvePointer) == ContinuationFlags::ResolvePointer)
|
||||
{
|
||||
return JsonSerializer::StoreFromPointer(output, object, nullptr, typeId, context);
|
||||
return JsonSerializer::StoreFromPointer(output, object, nullptr, typeId, useCustom, context);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -241,19 +249,19 @@ namespace AZ
|
||||
{
|
||||
return result;
|
||||
}
|
||||
return result.Combine(JsonSerializer::Store(output, object, nullptr, typeId, context));
|
||||
return result.Combine(JsonSerializer::Store(output, object, nullptr, typeId, useCustom, context));
|
||||
}
|
||||
else
|
||||
{
|
||||
void* defaultObjectPtr = AZStd::any_cast<void>(&newDefaultObject);
|
||||
return JsonSerializer::Store(output, object, defaultObjectPtr, typeId, context);
|
||||
return JsonSerializer::Store(output, object, defaultObjectPtr, typeId, useCustom, context);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return (flags & ContinuationFlags::ResolvePointer) == ContinuationFlags::ResolvePointer ?
|
||||
JsonSerializer::StoreFromPointer(output, object, defaultObject, typeId, context) :
|
||||
JsonSerializer::Store(output, object, defaultObject, typeId, context);
|
||||
JsonSerializer::StoreFromPointer(output, object, defaultObject, typeId, useCustom, context) :
|
||||
JsonSerializer::Store(output, object, defaultObject, typeId, useCustom, context);
|
||||
}
|
||||
|
||||
JsonSerializationResult::ResultCode BaseJsonSerializer::LoadTypeId(Uuid& typeId, const rapidjson::Value& input,
|
||||
|
||||
@@ -159,12 +159,13 @@ namespace AZ
|
||||
|
||||
enum class ContinuationFlags
|
||||
{
|
||||
None = 0, //! No extra flags.
|
||||
ResolvePointer = 1 << 0, //! The pointer passed in contains a pointer. The (de)serializer will attempt to resolve to an instance.
|
||||
ReplaceDefault = 1 << 1, //! The default value provided for storing will be replaced with a newly created one.
|
||||
LoadAsNewInstance = 1 << 2 //! Treats the value as if it's a newly created instance. This may trigger serializers marked with
|
||||
//! OperationFlags::InitializeNewInstance. Used for instance by pointers or new instances added to
|
||||
//! an array.
|
||||
None = 0, //! No extra flags.
|
||||
ResolvePointer = 1 << 0, //! The pointer passed in contains a pointer. The (de)serializer will attempt to resolve to an instance.
|
||||
ReplaceDefault = 1 << 1, //! The default value provided for storing will be replaced with a newly created one.
|
||||
LoadAsNewInstance = 1 << 2, //! Treats the value as if it's a newly created instance. This may trigger serializers marked with
|
||||
//! OperationFlags::InitializeNewInstance. Used for instance by pointers or new instances added to
|
||||
//! an array.
|
||||
IgnoreTypeSerializer = 1 << 3, //! Ignore the custom/specific serializer for the TypeId
|
||||
};
|
||||
|
||||
enum class OperationFlags
|
||||
|
||||
@@ -38,7 +38,8 @@ namespace AZ
|
||||
}
|
||||
|
||||
JsonSerializationResult::ResultCode JsonDeserializer::Load(
|
||||
void* object, const Uuid& typeId, const rapidjson::Value& value, bool isNewInstance, JsonDeserializerContext& context)
|
||||
void* object, const Uuid& typeId, const rapidjson::Value& value, bool isNewInstance, UseTypeDeserializer custom,
|
||||
JsonDeserializerContext& context)
|
||||
{
|
||||
using namespace AZ::JsonSerializationResult;
|
||||
|
||||
@@ -48,8 +49,8 @@ namespace AZ
|
||||
"Target object for Json Serialization is pointing to nothing during loading.");
|
||||
}
|
||||
|
||||
BaseJsonSerializer* serializer = context.GetRegistrationContext()->GetSerializerForType(typeId);
|
||||
if (serializer)
|
||||
if (BaseJsonSerializer* serializer
|
||||
= (custom == UseTypeDeserializer::Yes ? context.GetRegistrationContext()->GetSerializerForType(typeId) : nullptr))
|
||||
{
|
||||
return DeserializerDefaultCheck(serializer, object, typeId, value, isNewInstance, context);
|
||||
}
|
||||
@@ -70,8 +71,11 @@ namespace AZ
|
||||
// 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)
|
||||
|
||||
if (BaseJsonSerializer* serializer
|
||||
= (custom == UseTypeDeserializer::Yes)
|
||||
? context.GetRegistrationContext()->GetSerializerForType(classData->m_azRtti->GetGenericTypeId())
|
||||
: nullptr)
|
||||
{
|
||||
return DeserializerDefaultCheck(serializer, object, typeId, value, isNewInstance, context);
|
||||
}
|
||||
@@ -101,7 +105,7 @@ namespace AZ
|
||||
}
|
||||
|
||||
JsonSerializationResult::ResultCode JsonDeserializer::LoadToPointer(void* object, const Uuid& typeId,
|
||||
const rapidjson::Value& value, JsonDeserializerContext& context)
|
||||
const rapidjson::Value& value, UseTypeDeserializer useCustom, JsonDeserializerContext& context)
|
||||
{
|
||||
using namespace JsonSerializationResult;
|
||||
|
||||
@@ -134,7 +138,7 @@ namespace AZ
|
||||
const SerializeContext::ClassData* resolvedClassData = context.GetSerializeContext()->FindClassData(resolvedTypeId);
|
||||
if (resolvedClassData)
|
||||
{
|
||||
status = JsonDeserializer::Load(*objectPtr, resolvedTypeId, value, true, context);
|
||||
status = JsonDeserializer::Load(*objectPtr, resolvedTypeId, value, true, useCustom, context);
|
||||
|
||||
*objectPtr = resolvedClassData->m_azRtti->Cast(*objectPtr, typeId);
|
||||
|
||||
@@ -171,11 +175,11 @@ namespace AZ
|
||||
}
|
||||
AZ_Assert(classElement.m_azRtti->GetTypeId() == classElement.m_typeId,
|
||||
"Type id mismatch during deserialization of a json file. (%s vs %s)");
|
||||
return LoadToPointer(object, classElement.m_typeId, value, context);
|
||||
return LoadToPointer(object, classElement.m_typeId, value, UseTypeDeserializer::Yes, context);
|
||||
}
|
||||
else
|
||||
{
|
||||
return Load(object, classElement.m_typeId, value, false, context);
|
||||
return Load(object, classElement.m_typeId, value, false, UseTypeDeserializer::Yes, context);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -571,11 +575,23 @@ namespace AZ
|
||||
if (loadedTypeId.m_determination == TypeIdDetermination::FailedToDetermine ||
|
||||
loadedTypeId.m_determination == TypeIdDetermination::FailedDueToMultipleTypeIds)
|
||||
{
|
||||
AZStd::string_view message = loadedTypeId.m_determination == TypeIdDetermination::FailedDueToMultipleTypeIds ?
|
||||
"Unable to resolve provided type because the same name points to multiple types." :
|
||||
"Unable to resolve provided type.";
|
||||
status = context.Report(Tasks::RetrieveInfo, Outcomes::Unknown, message);
|
||||
return ResolvePointerResult::FullyProcessed;
|
||||
auto typeField = pointerData.FindMember(JsonSerialization::TypeIdFieldIdentifier);
|
||||
if (typeField != pointerData.MemberEnd() && typeField->value.IsString())
|
||||
{
|
||||
const char* format = loadedTypeId.m_determination == TypeIdDetermination::FailedToDetermine ?
|
||||
"Unable to resolve provided type: %.*s." :
|
||||
"Unable to resolve provided type %.*s because the same name points to multiple types.";
|
||||
status = context.Report(Tasks::RetrieveInfo, Outcomes::Unknown,
|
||||
AZStd::string::format(format, typeField->value.GetStringLength(), typeField->value.GetString()));
|
||||
}
|
||||
else
|
||||
{
|
||||
const char* message = loadedTypeId.m_determination == TypeIdDetermination::FailedToDetermine ?
|
||||
"Unable to resolve provided type." :
|
||||
"Unable to resolve provided type because the same name points to multiple types.";
|
||||
status = context.Report(Tasks::RetrieveInfo, Outcomes::Unknown, message);
|
||||
}
|
||||
return ResolvePointerResult::FullyProcessed;
|
||||
}
|
||||
|
||||
if (loadedTypeId.m_typeId != objectType)
|
||||
|
||||
@@ -28,6 +28,11 @@ namespace AZ
|
||||
FullyProcessed,
|
||||
ContinueProcessing
|
||||
};
|
||||
enum class UseTypeDeserializer : bool
|
||||
{
|
||||
No,
|
||||
Yes
|
||||
};
|
||||
enum class TypeIdDetermination : u8
|
||||
{
|
||||
ExplicitTypeId, // Type id was explicitly defined using "$type".
|
||||
@@ -55,10 +60,11 @@ namespace AZ
|
||||
JsonDeserializer(JsonDeserializer&& rhs) = delete;
|
||||
|
||||
static JsonSerializationResult::ResultCode Load(
|
||||
void* object, const Uuid& typeId, const rapidjson::Value& value, bool isNewInstance, JsonDeserializerContext& context);
|
||||
void* object, const Uuid& typeId, const rapidjson::Value& value, bool isNewInstance, UseTypeDeserializer useCustom,
|
||||
JsonDeserializerContext& context);
|
||||
|
||||
static JsonSerializationResult::ResultCode LoadToPointer(void* object, const Uuid& typeId, const rapidjson::Value& value,
|
||||
JsonDeserializerContext& context);
|
||||
UseTypeDeserializer useCustom, JsonDeserializerContext& context);
|
||||
|
||||
static JsonSerializationResult::ResultCode LoadWithClassElement(void* object, const rapidjson::Value& value,
|
||||
const SerializeContext::ClassElement& classElement, JsonDeserializerContext& context);
|
||||
|
||||
@@ -245,7 +245,7 @@ namespace AZ
|
||||
{
|
||||
StackedString path(StackedString::Format::JsonPointer);
|
||||
JsonDeserializerContext context(settings);
|
||||
result = JsonDeserializer::Load(object, objectType, root, false, context);
|
||||
result = JsonDeserializer::Load(object, objectType, root, false, JsonDeserializer::UseTypeDeserializer::Yes, context);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@@ -322,7 +322,7 @@ namespace AZ
|
||||
|
||||
JsonSerializerContext context(settings, allocator);
|
||||
StackedString path(StackedString::Format::ContextPath);
|
||||
result = JsonSerializer::Store(output, object, defaultObject, objectType, context);
|
||||
result = JsonSerializer::Store(output, object, defaultObject, objectType, JsonSerializer::UseTypeSerializer::Yes, context);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
namespace AZ
|
||||
{
|
||||
JsonSerializationResult::ResultCode JsonSerializer::Store(rapidjson::Value& output, const void* object, const void* defaultObject,
|
||||
const Uuid& typeId, JsonSerializerContext& context)
|
||||
const Uuid& typeId, UseTypeSerializer custom, JsonSerializerContext& context)
|
||||
{
|
||||
using namespace JsonSerializationResult;
|
||||
|
||||
@@ -32,8 +32,8 @@ namespace AZ
|
||||
|
||||
// First check if there's a generic serializer registered for this. This makes it possible to use serializers that
|
||||
// are not (directly) registered with the Serialize Context.
|
||||
auto serializer = context.GetRegistrationContext()->GetSerializerForType(typeId);
|
||||
if (serializer)
|
||||
if (BaseJsonSerializer* serializer
|
||||
= (custom == UseTypeSerializer::Yes ? context.GetRegistrationContext()->GetSerializerForType(typeId) : nullptr))
|
||||
{
|
||||
// Start by setting the object to be an explicit default.
|
||||
output.SetObject();
|
||||
@@ -57,17 +57,18 @@ namespace AZ
|
||||
"No factory available to create a default object for comparison.");
|
||||
}
|
||||
void* defaultObjectPtr = AZStd::any_cast<void>(&defaultObjectInstance);
|
||||
ResultCode conversionResult = StoreWithClassData(output, object, defaultObjectPtr, *classData, StoreTypeId::No, context);
|
||||
ResultCode conversionResult = StoreWithClassData(output, object, defaultObjectPtr, *classData, StoreTypeId::No
|
||||
, UseTypeSerializer::Yes, context);
|
||||
return ResultCode::Combine(result, conversionResult);
|
||||
}
|
||||
else
|
||||
{
|
||||
return StoreWithClassData(output, object, defaultObject, *classData, StoreTypeId::No, context);
|
||||
return StoreWithClassData(output, object, defaultObject, *classData, StoreTypeId::No, custom, context);
|
||||
}
|
||||
}
|
||||
|
||||
JsonSerializationResult::ResultCode JsonSerializer::StoreFromPointer(rapidjson::Value& output, const void* object,
|
||||
const void* defaultObject, const Uuid& typeId, JsonSerializerContext& context)
|
||||
const void* defaultObject, const Uuid& typeId, UseTypeSerializer custom, JsonSerializerContext& context)
|
||||
{
|
||||
using namespace JsonSerializationResult;
|
||||
|
||||
@@ -85,19 +86,21 @@ namespace AZ
|
||||
AZ_Assert(classData->m_azRtti->GetTypeId() == typeId, "Type id mismatch in '%s' during serialization to a json file. (%s vs %s)",
|
||||
classData->m_name, classData->m_azRtti->GetTypeId().ToString<AZStd::string>().c_str(), typeId.ToString<AZStd::string>().c_str());
|
||||
|
||||
return StoreWithClassDataFromPointer(output, object, defaultObject, *classData, context);
|
||||
return StoreWithClassDataFromPointer(output, object, defaultObject, *classData, custom, context);
|
||||
}
|
||||
|
||||
JsonSerializationResult::ResultCode JsonSerializer::StoreWithClassData(rapidjson::Value& node, const void* object,
|
||||
const void* defaultObject, const SerializeContext::ClassData& classData, StoreTypeId storeTypeId,
|
||||
JsonSerializerContext& context)
|
||||
UseTypeSerializer custom, JsonSerializerContext& context)
|
||||
{
|
||||
using namespace JsonSerializationResult;
|
||||
|
||||
// Start by setting the object to be an explicit default.
|
||||
node.SetObject();
|
||||
|
||||
auto serializer = context.GetRegistrationContext()->GetSerializerForType(classData.m_typeId);
|
||||
auto serializer = custom == UseTypeSerializer::Yes
|
||||
? context.GetRegistrationContext()->GetSerializerForType(classData.m_typeId) : nullptr;
|
||||
|
||||
if (serializer)
|
||||
{
|
||||
ResultCode result = serializer->Store(node, object, defaultObject, classData.m_typeId, context);
|
||||
@@ -153,7 +156,7 @@ namespace AZ
|
||||
}
|
||||
|
||||
JsonSerializationResult::ResultCode JsonSerializer::StoreWithClassDataFromPointer(rapidjson::Value& output, const void* object,
|
||||
const void* defaultObject, const SerializeContext::ClassData& classData, JsonSerializerContext& context)
|
||||
const void* defaultObject, const SerializeContext::ClassData& classData, UseTypeSerializer custom, JsonSerializerContext& context)
|
||||
{
|
||||
using namespace JsonSerializationResult;
|
||||
|
||||
@@ -175,7 +178,7 @@ namespace AZ
|
||||
}
|
||||
else
|
||||
{
|
||||
return StoreWithClassData(output, object, defaultObject, *resolvedClassData, storeTypeId, context);
|
||||
return StoreWithClassData(output, object, defaultObject, *resolvedClassData, storeTypeId, custom, context);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -220,8 +223,8 @@ namespace AZ
|
||||
{
|
||||
rapidjson::Value value;
|
||||
ResultCode result = classElement.m_flags & SerializeContext::ClassElement::FLG_POINTER ?
|
||||
StoreWithClassDataFromPointer(value, object, defaultObject, *elementClassData, context):
|
||||
StoreWithClassData(value, object, defaultObject, *elementClassData, StoreTypeId::No, context);
|
||||
StoreWithClassDataFromPointer(value, object, defaultObject, *elementClassData, UseTypeSerializer::Yes, context):
|
||||
StoreWithClassData(value, object, defaultObject, *elementClassData, StoreTypeId::No, UseTypeSerializer::Yes, context);
|
||||
if (result.GetProcessing() != Processing::Halted)
|
||||
{
|
||||
if (parentNode.IsObject())
|
||||
|
||||
@@ -26,6 +26,11 @@ namespace AZ
|
||||
No,
|
||||
Yes
|
||||
};
|
||||
enum class UseTypeSerializer : bool
|
||||
{
|
||||
No,
|
||||
Yes
|
||||
};
|
||||
enum class ResolvePointerResult
|
||||
{
|
||||
FullyProcessed,
|
||||
@@ -41,16 +46,18 @@ namespace AZ
|
||||
JsonSerializer(JsonSerializer&& rhs) = delete;
|
||||
|
||||
static JsonSerializationResult::ResultCode Store(rapidjson::Value& output, const void* object, const void* defaultObject,
|
||||
const Uuid& typeId, JsonSerializerContext& context);
|
||||
const Uuid& typeId, UseTypeSerializer useCustom, JsonSerializerContext& context);
|
||||
|
||||
static JsonSerializationResult::ResultCode StoreFromPointer(rapidjson::Value& output, const void* object, const void* defaultObject,
|
||||
const Uuid& typeId, JsonSerializerContext& context);
|
||||
const Uuid& typeId, UseTypeSerializer custom, JsonSerializerContext& context);
|
||||
|
||||
static JsonSerializationResult::ResultCode StoreWithClassData(rapidjson::Value& node, const void* object, const void* defaultObject,
|
||||
const SerializeContext::ClassData& classData, StoreTypeId storeTypeId, JsonSerializerContext& context);
|
||||
const SerializeContext::ClassData& classData, StoreTypeId storeTypeId, UseTypeSerializer custom,
|
||||
JsonSerializerContext& context);
|
||||
|
||||
static JsonSerializationResult::ResultCode StoreWithClassDataFromPointer(rapidjson::Value& output, const void* object,
|
||||
const void* defaultObject, const SerializeContext::ClassData& classData, JsonSerializerContext& context);
|
||||
const void* defaultObject, const SerializeContext::ClassData& classData, UseTypeSerializer custom,
|
||||
JsonSerializerContext& context);
|
||||
|
||||
static JsonSerializationResult::ResultCode StoreWithClassElement(rapidjson::Value& parentNode, const void* object,
|
||||
const void* defaultObject, const SerializeContext::ClassElement& classElement, JsonSerializerContext& context);
|
||||
|
||||
@@ -0,0 +1,412 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project.
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
#include <AzCore/Utils/Utils.h>
|
||||
#include <AzCore/base.h>
|
||||
#include <AzCore/Component/ComponentApplicationBus.h>
|
||||
#include <AzCore/IO/ByteContainerStream.h>
|
||||
#include <AzCore/IO/FileIO.h>
|
||||
#include <AzCore/IO/GenericStreams.h>
|
||||
#include <AzCore/IO/SystemFile.h>
|
||||
#include <AzCore/IO/TextStreamWriters.h>
|
||||
#include <AzCore/JSON/error/error.h>
|
||||
#include <AzCore/JSON/error/en.h>
|
||||
#include <AzCore/JSON/prettywriter.h>
|
||||
#include <AzCore/Memory/OSAllocator.h>
|
||||
#include <AzCore/Serialization/Json/JsonSerialization.h>
|
||||
#include <AzCore/Serialization/Utils.h>
|
||||
#include <AzCore/Utils/Utils.h>
|
||||
|
||||
#include <AzCore/Serialization/Json/JsonUtils.h>
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
namespace JsonSerializationUtils
|
||||
{
|
||||
static const char* FileTypeTag = "Type";
|
||||
static const char* FileType = "JsonSerialization";
|
||||
static const char* VersionTag = "Version";
|
||||
static const char* ClassNameTag = "ClassName";
|
||||
static const char* ClassDataTag = "ClassData";
|
||||
|
||||
AZ::Outcome<void, AZStd::string> WriteJsonString(const rapidjson::Document& document, AZStd::string& jsonText, WriteJsonSettings settings)
|
||||
{
|
||||
AZ::IO::ByteContainerStream<AZStd::string> stream{&jsonText};
|
||||
return WriteJsonStream(document, stream, settings);
|
||||
}
|
||||
|
||||
AZ::Outcome<void, AZStd::string> WriteJsonFile(const rapidjson::Document& document, AZStd::string_view filePath, WriteJsonSettings settings)
|
||||
{
|
||||
// Write the json into memory first and then write the file, rather than passing a file stream to rapidjson.
|
||||
// This should avoid creating a large number of micro-writes to the file.
|
||||
AZStd::string fileContent;
|
||||
auto outcome = WriteJsonString(document, fileContent, settings);
|
||||
if (!outcome.IsSuccess())
|
||||
{
|
||||
return outcome;
|
||||
}
|
||||
|
||||
return AZ::Utils::WriteFile(fileContent, filePath);
|
||||
}
|
||||
|
||||
AZ::Outcome<void, AZStd::string> WriteJsonStream(const rapidjson::Document& document, IO::GenericStream& stream, WriteJsonSettings settings)
|
||||
{
|
||||
AZ::IO::RapidJSONStreamWriter jsonStreamWriter(&stream);
|
||||
|
||||
rapidjson::PrettyWriter<AZ::IO::RapidJSONStreamWriter> writer(jsonStreamWriter);
|
||||
|
||||
if (settings.m_maxDecimalPlaces >= 0)
|
||||
{
|
||||
writer.SetMaxDecimalPlaces(settings.m_maxDecimalPlaces);
|
||||
}
|
||||
|
||||
if (document.Accept(writer))
|
||||
{
|
||||
return AZ::Success();
|
||||
}
|
||||
else
|
||||
{
|
||||
return AZ::Failure(AZStd::string{"Json Writer failed"});
|
||||
}
|
||||
}
|
||||
|
||||
AZ::Outcome<void, AZStd::string> SaveObjectToStreamByType(const void* objectPtr, const Uuid& classId, IO::GenericStream& stream,
|
||||
const void* defaultObjectPtr, const JsonSerializerSettings* settings)
|
||||
{
|
||||
if (!stream.CanWrite())
|
||||
{
|
||||
return AZ::Failure(AZStd::string("The GenericStream can't be written to"));
|
||||
}
|
||||
|
||||
JsonSerializerSettings saveSettings;
|
||||
if (settings)
|
||||
{
|
||||
saveSettings = *settings;
|
||||
}
|
||||
|
||||
AZ::SerializeContext* serializeContext = saveSettings.m_serializeContext;
|
||||
if (!serializeContext)
|
||||
{
|
||||
AZ::ComponentApplicationBus::BroadcastResult(serializeContext, &AZ::ComponentApplicationBus::Events::GetSerializeContext);
|
||||
if (!serializeContext)
|
||||
{
|
||||
return AZ::Failure(AZStd::string::format("Need SerializeContext for saving"));
|
||||
}
|
||||
saveSettings.m_serializeContext = serializeContext;
|
||||
}
|
||||
|
||||
rapidjson::Document jsonDocument;
|
||||
jsonDocument.SetObject();
|
||||
jsonDocument.AddMember(rapidjson::StringRef(FileTypeTag), rapidjson::StringRef(FileType), jsonDocument.GetAllocator());
|
||||
|
||||
rapidjson::Value serializedObject;
|
||||
|
||||
JsonSerializationResult::ResultCode jsonResult = JsonSerialization::Store(serializedObject, jsonDocument.GetAllocator(),
|
||||
objectPtr, defaultObjectPtr, classId, saveSettings);
|
||||
|
||||
if (jsonResult.GetProcessing() != JsonSerializationResult::Processing::Completed)
|
||||
{
|
||||
return AZ::Failure(jsonResult.ToString(""));
|
||||
}
|
||||
|
||||
const SerializeContext::ClassData* classData = serializeContext->FindClassData(classId);
|
||||
|
||||
jsonDocument.AddMember(rapidjson::StringRef(VersionTag), 1, jsonDocument.GetAllocator());
|
||||
jsonDocument.AddMember(rapidjson::StringRef(ClassNameTag), rapidjson::StringRef(classData->m_name), jsonDocument.GetAllocator());
|
||||
jsonDocument.AddMember(rapidjson::StringRef(ClassDataTag), AZStd::move(serializedObject), jsonDocument.GetAllocator());
|
||||
|
||||
AZ::IO::RapidJSONStreamWriter jsonStreamWriter(&stream);
|
||||
rapidjson::PrettyWriter<AZ::IO::RapidJSONStreamWriter> writer(jsonStreamWriter);
|
||||
bool jsonWriteResult = jsonDocument.Accept(writer);
|
||||
if (!jsonWriteResult)
|
||||
{
|
||||
return AZ::Failure(AZStd::string::format("Unable to write class %s with json serialization format'",
|
||||
classId.ToString<AZStd::string>().data()));
|
||||
}
|
||||
|
||||
return AZ::Success();
|
||||
}
|
||||
|
||||
AZ::Outcome<void, AZStd::string> SaveObjectToFileByType(const void* classPtr, const Uuid& classId, const AZStd::string& filePath,
|
||||
const void* defaultClassPtr, const JsonSerializerSettings* settings)
|
||||
{
|
||||
AZStd::vector<char> buffer;
|
||||
buffer.reserve(1024);
|
||||
AZ::IO::ByteContainerStream<AZStd::vector<char> > byteStream(&buffer);
|
||||
auto saveResult = SaveObjectToStreamByType(classPtr, classId, byteStream, defaultClassPtr, settings);
|
||||
if (saveResult.IsSuccess())
|
||||
{
|
||||
AZ::IO::FileIOStream outputFileStream;
|
||||
if (!outputFileStream.Open(filePath.c_str(), AZ::IO::OpenMode::ModeWrite | AZ::IO::OpenMode::ModeCreatePath | AZ::IO::OpenMode::ModeText))
|
||||
{
|
||||
return AZ::Failure(AZStd::string::format("Error opening file '%s' for writing", filePath.c_str()));
|
||||
}
|
||||
outputFileStream.Write(buffer.size(), buffer.data());
|
||||
}
|
||||
return saveResult;
|
||||
}
|
||||
|
||||
// Helper function to check whether the load outcome was success (for loading json serialization file)
|
||||
bool WasLoadSuccess(JsonSerializationResult::Outcomes outcome)
|
||||
{
|
||||
return (outcome == JsonSerializationResult::Outcomes::Success
|
||||
|| outcome == JsonSerializationResult::Outcomes::DefaultsUsed
|
||||
|| outcome == JsonSerializationResult::Outcomes::PartialDefaults);
|
||||
}
|
||||
|
||||
AZ::Outcome<void, AZStd::string> PrepareDeserializerSettings(const JsonDeserializerSettings* inputSettings, JsonDeserializerSettings& returnSettings
|
||||
, AZStd::string& deserializeError)
|
||||
{
|
||||
if (inputSettings)
|
||||
{
|
||||
returnSettings = *inputSettings;
|
||||
}
|
||||
|
||||
if (!returnSettings.m_serializeContext)
|
||||
{
|
||||
AZ::ComponentApplicationBus::BroadcastResult(returnSettings.m_serializeContext, &AZ::ComponentApplicationBus::Events::GetSerializeContext);
|
||||
if (!returnSettings.m_serializeContext)
|
||||
{
|
||||
return AZ::Failure(AZStd::string("Need SerializeContext for loading"));
|
||||
}
|
||||
}
|
||||
|
||||
// Report unused data field as error by default
|
||||
auto reporting = returnSettings.m_reporting;
|
||||
auto issueReportingCallback = [&deserializeError, reporting](AZStd::string_view message, JsonSerializationResult::ResultCode result, AZStd::string_view target) -> JsonSerializationResult::ResultCode
|
||||
{
|
||||
using namespace JsonSerializationResult;
|
||||
|
||||
if (!WasLoadSuccess(result.GetOutcome()))
|
||||
{
|
||||
// This if is a hack around fault in the JSON serialization system
|
||||
// Jira: LY-106587
|
||||
if (message != "No part of the string could be interpreted as a uuid.")
|
||||
{
|
||||
deserializeError.append(message);
|
||||
deserializeError.append(AZStd::string::format(" '%s' \n", target.data()));
|
||||
}
|
||||
}
|
||||
|
||||
if (reporting)
|
||||
{
|
||||
result = reporting(message, result, target);
|
||||
}
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
returnSettings.m_reporting = issueReportingCallback;
|
||||
|
||||
return AZ::Success();
|
||||
}
|
||||
|
||||
|
||||
AZ::Outcome<rapidjson::Document, AZStd::string> ReadJsonString(AZStd::string_view jsonText)
|
||||
{
|
||||
rapidjson::Document jsonDocument;
|
||||
jsonDocument.Parse<rapidjson::kParseCommentsFlag>(jsonText.data(), jsonText.size());
|
||||
if (jsonDocument.HasParseError())
|
||||
{
|
||||
size_t lineNumber = 1;
|
||||
|
||||
const size_t errorOffset = jsonDocument.GetErrorOffset();
|
||||
for (size_t searchOffset = jsonText.find('\n');
|
||||
searchOffset < errorOffset && searchOffset < AZStd::string::npos;
|
||||
searchOffset = jsonText.find('\n', searchOffset + 1))
|
||||
{
|
||||
lineNumber++;
|
||||
}
|
||||
|
||||
return AZ::Failure(AZStd::string::format("JSON parse error at line %zu: %s", lineNumber, rapidjson::GetParseError_En(jsonDocument.GetParseError())));
|
||||
}
|
||||
else
|
||||
{
|
||||
return AZ::Success(AZStd::move(jsonDocument));
|
||||
}
|
||||
}
|
||||
|
||||
AZ::Outcome<rapidjson::Document, AZStd::string> ReadJsonStream(IO::GenericStream& stream)
|
||||
{
|
||||
IO::SizeType length = stream.GetLength();
|
||||
|
||||
if (length > AZ::Utils::DefaultMaxFileSize)
|
||||
{
|
||||
return AZ::Failure(AZStd::string{ "Data is too large." });
|
||||
}
|
||||
|
||||
AZStd::vector<char> memoryBuffer;
|
||||
memoryBuffer.resize_no_construct(static_cast<AZStd::vector<char>::size_type>(static_cast<AZStd::vector<char>::size_type>(length) + 1));
|
||||
|
||||
IO::SizeType bytesRead = stream.Read(length, memoryBuffer.data());
|
||||
if (bytesRead != length)
|
||||
{
|
||||
return AZ::Failure(AZStd::string{"Cannot to read input stream."});
|
||||
}
|
||||
|
||||
memoryBuffer.back() = 0;
|
||||
|
||||
return ReadJsonString(AZStd::string_view{memoryBuffer.data(), memoryBuffer.size()});
|
||||
}
|
||||
|
||||
AZ::Outcome<rapidjson::Document, AZStd::string> ReadJsonFile(AZStd::string_view filePath)
|
||||
{
|
||||
// Read into memory first and then parse the json, rather than passing a file stream to rapidjson.
|
||||
// This should avoid creating a large number of micro-reads from the file.
|
||||
|
||||
auto readResult = AZ::Utils::ReadFile<AZStd::string>(filePath);
|
||||
if(!readResult.IsSuccess())
|
||||
{
|
||||
return AZ::Failure(readResult.GetError());
|
||||
}
|
||||
|
||||
AZStd::string jsonContent = readResult.TakeValue();
|
||||
|
||||
auto result = ReadJsonString(jsonContent);
|
||||
if (!result.IsSuccess())
|
||||
{
|
||||
return AZ::Failure(AZStd::string::format("Failed to load '%.*s'. %s", AZ_STRING_ARG(filePath), result.GetError().c_str()));
|
||||
}
|
||||
else
|
||||
{
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
// Helper function to validate the JSON is structured with the standard header for a generic class
|
||||
AZ::Outcome<void, AZStd::string> ValidateJsonClassHeader(const rapidjson::Document& jsonDocument)
|
||||
{
|
||||
auto typeItr = jsonDocument.FindMember(FileTypeTag);
|
||||
if (typeItr == jsonDocument.MemberEnd() || !typeItr->value.IsString() || azstricmp(typeItr->value.GetString(), FileType) != 0)
|
||||
{
|
||||
return AZ::Failure(AZStd::string::format("Not a valid JsonSerialization file"));
|
||||
}
|
||||
|
||||
auto nameItr = jsonDocument.FindMember(ClassNameTag);
|
||||
if (nameItr == jsonDocument.MemberEnd() || !nameItr->value.IsString())
|
||||
{
|
||||
return AZ::Failure(AZStd::string::format("File should contain ClassName"));
|
||||
}
|
||||
|
||||
auto dataItr = jsonDocument.FindMember(ClassDataTag);
|
||||
// data can be empty but it should be an object
|
||||
if (dataItr != jsonDocument.MemberEnd() && !dataItr->value.IsObject())
|
||||
{
|
||||
return AZ::Failure(AZStd::string::format("ClassData should be an object"));
|
||||
}
|
||||
|
||||
return AZ::Success();
|
||||
}
|
||||
|
||||
AZ::Outcome<void, AZStd::string> LoadObjectFromStreamByType(void* objectToLoad, const Uuid& classId, IO::GenericStream& stream,
|
||||
const JsonDeserializerSettings* settings)
|
||||
{
|
||||
JsonDeserializerSettings loadSettings;
|
||||
AZStd::string deserializeErrors;
|
||||
auto prepare = PrepareDeserializerSettings(settings, loadSettings, deserializeErrors);
|
||||
if (!prepare.IsSuccess())
|
||||
{
|
||||
return AZ::Failure(prepare.GetError());
|
||||
}
|
||||
|
||||
auto parseResult = ReadJsonStream(stream);
|
||||
if (!parseResult.IsSuccess())
|
||||
{
|
||||
return AZ::Failure(parseResult.GetError());
|
||||
}
|
||||
|
||||
const rapidjson::Document& jsonDocument = parseResult.GetValue();
|
||||
|
||||
auto validateResult = ValidateJsonClassHeader(jsonDocument);
|
||||
if (!validateResult.IsSuccess())
|
||||
{
|
||||
return AZ::Failure(validateResult.GetError());
|
||||
}
|
||||
|
||||
const char* className = jsonDocument.FindMember(ClassNameTag)->value.GetString();
|
||||
|
||||
// validate class name
|
||||
auto classData = loadSettings.m_serializeContext->FindClassData(classId);
|
||||
if (!classData)
|
||||
{
|
||||
return AZ::Failure(AZStd::string::format("Try to load class from Id %s", classId.ToString<AZStd::string>().c_str()));
|
||||
}
|
||||
|
||||
if (azstricmp(classData->m_name, className) != 0)
|
||||
{
|
||||
return AZ::Failure(AZStd::string::format("Try to load class %s from class %s data", classData->m_name, className));
|
||||
}
|
||||
|
||||
JsonSerializationResult::ResultCode result = JsonSerialization::Load(objectToLoad, classId, jsonDocument.FindMember(ClassDataTag)->value, loadSettings);
|
||||
|
||||
if (!WasLoadSuccess(result.GetOutcome()) || !deserializeErrors.empty())
|
||||
{
|
||||
return AZ::Failure(deserializeErrors);
|
||||
}
|
||||
|
||||
return AZ::Success();
|
||||
}
|
||||
|
||||
AZ::Outcome<AZStd::any, AZStd::string> LoadAnyObjectFromStream(IO::GenericStream& stream, const JsonDeserializerSettings* settings)
|
||||
{
|
||||
JsonDeserializerSettings loadSettings;
|
||||
AZStd::string deserializeErrors;
|
||||
auto prepare = PrepareDeserializerSettings(settings, loadSettings, deserializeErrors);
|
||||
if (!prepare.IsSuccess())
|
||||
{
|
||||
return AZ::Failure(prepare.GetError());
|
||||
}
|
||||
|
||||
auto parseResult = ReadJsonStream(stream);
|
||||
if (!parseResult.IsSuccess())
|
||||
{
|
||||
return AZ::Failure(parseResult.GetError());
|
||||
}
|
||||
|
||||
const rapidjson::Document& jsonDocument = parseResult.GetValue();
|
||||
|
||||
auto validateResult = ValidateJsonClassHeader(jsonDocument);
|
||||
if (!parseResult.IsSuccess())
|
||||
{
|
||||
return AZ::Failure(parseResult.GetError());
|
||||
}
|
||||
|
||||
const char* className = jsonDocument.FindMember(ClassNameTag)->value.GetString();
|
||||
AZStd::vector<AZ::Uuid> ids = loadSettings.m_serializeContext->FindClassId(AZ::Crc32(className));
|
||||
|
||||
// Load with first found class id
|
||||
if (ids.size() >= 1)
|
||||
{
|
||||
auto classId = ids[0];
|
||||
AZStd::any anyData = loadSettings.m_serializeContext->CreateAny(classId);
|
||||
auto& objectData = jsonDocument.FindMember(ClassDataTag)->value;
|
||||
JsonSerializationResult::ResultCode result = JsonSerialization::Load(AZStd::any_cast<void>(&anyData), classId, objectData, loadSettings);
|
||||
|
||||
if (!WasLoadSuccess(result.GetOutcome()) || !deserializeErrors.empty())
|
||||
{
|
||||
return AZ::Failure(deserializeErrors);
|
||||
}
|
||||
|
||||
return AZ::Success(anyData);
|
||||
}
|
||||
|
||||
return AZ::Failure(AZStd::string::format("Can't find serialize context for class %s", className));
|
||||
}
|
||||
|
||||
AZ::Outcome<AZStd::any, AZStd::string> LoadAnyObjectFromFile(const AZStd::string& filePath, const JsonDeserializerSettings* settings)
|
||||
{
|
||||
AZ::IO::FileIOStream inputFileStream;
|
||||
if (!inputFileStream.Open(filePath.c_str(), AZ::IO::OpenMode::ModeRead | AZ::IO::OpenMode::ModeText))
|
||||
{
|
||||
return AZ::Failure(AZStd::string::format("Error opening file '%s' for reading", filePath.c_str()));
|
||||
}
|
||||
return LoadAnyObjectFromStream(inputFileStream, settings);
|
||||
}
|
||||
|
||||
} // namespace JsonSerializationUtils
|
||||
} // namespace AZ
|
||||
@@ -0,0 +1,105 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project.
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <AzCore/Serialization/ObjectStream.h>
|
||||
#include <AzCore/Asset/AssetCommon.h>
|
||||
#include <AzCore/std/string/string.h>
|
||||
#include <AzCore/IO/FileIO.h>
|
||||
#include <AzCore/JSON/document.h>
|
||||
#include <AzCore/JSON/writer.h>
|
||||
#include <AzCore/Serialization/Json/JsonSerializationResult.h>
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
namespace IO
|
||||
{
|
||||
class GenericStream;
|
||||
}
|
||||
|
||||
struct JsonSerializerSettings;
|
||||
struct JsonDeserializerSettings;
|
||||
|
||||
// Utility functions which use json serializer/deserializer to save/load object to file/stream
|
||||
namespace JsonSerializationUtils
|
||||
{
|
||||
struct WriteJsonSettings
|
||||
{
|
||||
int m_maxDecimalPlaces = -1; // -1 means use default
|
||||
};
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// Save functions
|
||||
|
||||
//! Save a json document to text. Otherwise returns a failure with error message.
|
||||
AZ::Outcome<void, AZStd::string> WriteJsonString(const rapidjson::Document& document, AZStd::string& jsonText, WriteJsonSettings settings = WriteJsonSettings{});
|
||||
|
||||
//! Save a json document to a file. Otherwise returns a failure with error message.
|
||||
AZ::Outcome<void, AZStd::string> WriteJsonFile(const rapidjson::Document& document, AZStd::string_view filePath, WriteJsonSettings settings = WriteJsonSettings{});
|
||||
|
||||
//! Save a json document to a stream. Otherwise returns a failure with error message.
|
||||
AZ::Outcome<void, AZStd::string> WriteJsonStream(const rapidjson::Document& document, IO::GenericStream& stream, WriteJsonSettings settings = WriteJsonSettings{});
|
||||
|
||||
AZ::Outcome<void, AZStd::string> SaveObjectToStreamByType(const void* objectPtr, const Uuid& objectType, IO::GenericStream& stream,
|
||||
const void* defaultObjectPtr = nullptr, const JsonSerializerSettings* settings = nullptr);
|
||||
AZ::Outcome<void, AZStd::string> SaveObjectToFileByType(const void* objectPtr, const Uuid& objectType, const AZStd::string& filePath,
|
||||
const void* defaultObjectPtr = nullptr, const JsonSerializerSettings* settings = nullptr);
|
||||
|
||||
template <typename ObjectType>
|
||||
AZ::Outcome<void, AZStd::string> SaveObjectToStream(const ObjectType* classPtr, IO::GenericStream& stream,
|
||||
const ObjectType* defaultClassPtr = nullptr, const JsonSerializerSettings* settings = nullptr)
|
||||
{
|
||||
return SaveObjectToStreamByType(classPtr, AzTypeInfo<ObjectType>::Uuid(), stream, defaultClassPtr, settings);
|
||||
}
|
||||
|
||||
template <typename ObjectType>
|
||||
AZ::Outcome<void, AZStd::string> SaveObjectToFile(const ObjectType* classPtr, const AZStd::string& filePath,
|
||||
const ObjectType* defaultClassPtr = nullptr, const JsonSerializerSettings* settings = nullptr)
|
||||
{
|
||||
return SaveObjectToFileByType(classPtr, AzTypeInfo<ObjectType>::Uuid(), filePath, defaultClassPtr, settings);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// Load functions
|
||||
|
||||
//! Parse json text. Returns a failure with error message if the content is not valid JSON.
|
||||
AZ::Outcome<rapidjson::Document, AZStd::string> ReadJsonString(AZStd::string_view jsonText);
|
||||
|
||||
//! Parse a json file. Returns a failure with error message if the content is not valid JSON.
|
||||
AZ::Outcome<rapidjson::Document, AZStd::string> ReadJsonFile(AZStd::string_view filePath);
|
||||
|
||||
//! Parse a json stream. Returns a failure with error message if the content is not valid JSON.
|
||||
AZ::Outcome<rapidjson::Document, AZStd::string> ReadJsonStream(IO::GenericStream& stream);
|
||||
|
||||
//! Load object with known class type
|
||||
AZ::Outcome<void, AZStd::string> LoadObjectFromStreamByType(void* objectToLoad, const Uuid& objectType, IO::GenericStream& stream,
|
||||
const JsonDeserializerSettings* settings = nullptr);
|
||||
|
||||
template <typename ObjectType>
|
||||
AZ::Outcome<void, AZStd::string> LoadObjectFromStream(ObjectType& objectToLoad, IO::GenericStream& stream, const JsonDeserializerSettings* settings = nullptr)
|
||||
{
|
||||
return LoadObjectFromStreamByType(&objectToLoad, AzTypeInfo<ObjectType>::Uuid(), stream, settings);
|
||||
}
|
||||
|
||||
template <typename ObjectType>
|
||||
AZ::Outcome<void, AZStd::string> LoadObjectFromFile(ObjectType& objectToLoad, const AZStd::string& filePath, const JsonDeserializerSettings* settings = nullptr)
|
||||
{
|
||||
AZ::IO::FileIOStream inputFileStream;
|
||||
if (!inputFileStream.Open(filePath.c_str(), AZ::IO::OpenMode::ModeRead | AZ::IO::OpenMode::ModeText))
|
||||
{
|
||||
return AZ::Failure(AZStd::string::format("Error opening file '%s' for reading", filePath.c_str()));
|
||||
}
|
||||
return LoadObjectFromStream(objectToLoad, inputFileStream, settings);
|
||||
}
|
||||
|
||||
//! Load any object
|
||||
AZ::Outcome<AZStd::any, AZStd::string> LoadAnyObjectFromStream(IO::GenericStream& stream, const JsonDeserializerSettings* settings = nullptr);
|
||||
AZ::Outcome<AZStd::any, AZStd::string> LoadAnyObjectFromFile(const AZStd::string& filePath,const JsonDeserializerSettings* settings = nullptr);
|
||||
|
||||
} // namespace JsonSerializationUtils
|
||||
} // namespace Az
|
||||
@@ -531,6 +531,8 @@ set(FILES
|
||||
Serialization/Json/JsonStringConversionUtils.h
|
||||
Serialization/Json/JsonSystemComponent.h
|
||||
Serialization/Json/JsonSystemComponent.cpp
|
||||
Serialization/Json/JsonUtils.h
|
||||
Serialization/Json/JsonUtils.cpp
|
||||
Serialization/Json/MapSerializer.h
|
||||
Serialization/Json/MapSerializer.cpp
|
||||
Serialization/Json/RegistrationContext.h
|
||||
|
||||
@@ -0,0 +1,492 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project.
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
#include <AzCore/Serialization/Json/JsonSerialization.h>
|
||||
#include <AzCore/Serialization/Json/JsonSystemComponent.h>
|
||||
#include <AzCore/Serialization/Json/RegistrationContext.h>
|
||||
#include <AzCore/UnitTest/TestTypes.h>
|
||||
|
||||
#include <AzTest/AzTest.h>
|
||||
|
||||
#include <AzCore/Serialization/Json/JsonUtils.h>
|
||||
|
||||
namespace UnitTest
|
||||
{
|
||||
using namespace AZ;
|
||||
|
||||
namespace Test1
|
||||
{
|
||||
class TestClass
|
||||
{
|
||||
public:
|
||||
AZ_TYPE_INFO(TestClass, "{731F8B22-086E-4CDE-9645-23078C6277C1}");
|
||||
static void Reflect(AZ::ReflectContext* context)
|
||||
{
|
||||
if (auto* serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
|
||||
{
|
||||
serializeContext->Class<TestClass>()
|
||||
->Version(1)
|
||||
->Field("int", &TestClass::m_int)
|
||||
->Field("float", &TestClass::m_float)
|
||||
->Field("string", &TestClass::m_string)
|
||||
->Field("unordered_map", &TestClass::m_unorderedMap)
|
||||
->Field("array", &TestClass::m_array)
|
||||
->Field("vector", &TestClass::m_vector)
|
||||
;
|
||||
}
|
||||
}
|
||||
|
||||
int m_int = 0;
|
||||
float m_float = 0;
|
||||
AZStd::string m_string = "TestClass";
|
||||
AZStd::unordered_map<int, AZStd::string> m_unorderedMap;
|
||||
AZStd::array<AZStd::string, 2> m_array;
|
||||
AZStd::vector<AZStd::string> m_vector;
|
||||
|
||||
void Init()
|
||||
{
|
||||
m_unorderedMap.emplace(1, "one");
|
||||
m_unorderedMap.emplace(5, "five");
|
||||
m_array[1] = "ONE";
|
||||
m_vector.push_back("anything");
|
||||
m_vector.push_back("something");
|
||||
}
|
||||
|
||||
bool operator == (const TestClass& other) const
|
||||
{
|
||||
return m_int == other.m_int
|
||||
&& m_float == other.m_float
|
||||
&& m_string == other.m_string
|
||||
&& m_unorderedMap == other.m_unorderedMap
|
||||
&& m_array == other.m_array
|
||||
&& m_vector == other.m_vector
|
||||
;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
namespace Test2
|
||||
{
|
||||
// Test class which has same class name with TestClass but difference class id reflected in SerializeContext
|
||||
class TestClass
|
||||
{
|
||||
public:
|
||||
AZ_TYPE_INFO(TestClass, "{DAC825C5-AB14-4D9D-AAC2-124E56E1F8FD}");
|
||||
static void Reflect(AZ::ReflectContext* context)
|
||||
{
|
||||
if (auto* serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
|
||||
{
|
||||
serializeContext->Class<TestClass>()
|
||||
->Version(1)
|
||||
->Field("SomeData", &TestClass::m_someData)
|
||||
;
|
||||
}
|
||||
}
|
||||
|
||||
int m_someData = 0;
|
||||
};
|
||||
}
|
||||
|
||||
class JsonSerializationUtilsTests
|
||||
: public AllocatorsTestFixture
|
||||
{
|
||||
protected:
|
||||
void SetUp() override
|
||||
{
|
||||
AllocatorsTestFixture::SetUp();
|
||||
|
||||
m_serializeContext = AZStd::make_unique<AZ::SerializeContext>();
|
||||
m_jsonRegistrationContext = AZStd::make_unique<AZ::JsonRegistrationContext>();
|
||||
m_jsonSystemComponent = AZStd::make_unique<AZ::JsonSystemComponent>();
|
||||
|
||||
m_serializationSettings.m_serializeContext = m_serializeContext.get();
|
||||
m_serializationSettings.m_registrationContext = m_jsonRegistrationContext.get();
|
||||
|
||||
m_deserializationSettings.m_serializeContext = m_serializeContext.get();
|
||||
m_deserializationSettings.m_registrationContext = m_jsonRegistrationContext.get();
|
||||
|
||||
m_jsonSystemComponent->Reflect(m_jsonRegistrationContext.get());
|
||||
Test1::TestClass::Reflect(m_serializeContext.get());
|
||||
Test2::TestClass::Reflect(m_serializeContext.get());
|
||||
}
|
||||
|
||||
void TearDown() override
|
||||
{
|
||||
m_jsonRegistrationContext->EnableRemoveReflection();
|
||||
m_jsonSystemComponent->Reflect(m_jsonRegistrationContext.get());
|
||||
m_jsonRegistrationContext->DisableRemoveReflection();
|
||||
|
||||
m_serializeContext->EnableRemoveReflection();
|
||||
Test1::TestClass::Reflect(m_serializeContext.get());
|
||||
Test2::TestClass::Reflect(m_serializeContext.get());
|
||||
m_serializeContext->DisableRemoveReflection();
|
||||
|
||||
m_jsonRegistrationContext.reset();
|
||||
m_serializeContext.reset();
|
||||
m_jsonSystemComponent.reset();
|
||||
|
||||
AllocatorsTestFixture::TearDown();
|
||||
}
|
||||
|
||||
AZStd::unique_ptr<SerializeContext> m_serializeContext;
|
||||
AZStd::unique_ptr<JsonRegistrationContext> m_jsonRegistrationContext;
|
||||
AZStd::unique_ptr<JsonSystemComponent> m_jsonSystemComponent;
|
||||
|
||||
JsonSerializerSettings m_serializationSettings;
|
||||
JsonDeserializerSettings m_deserializationSettings;
|
||||
};
|
||||
|
||||
TEST_F(JsonSerializationUtilsTests, SaveLoadObjectToStream_Success)
|
||||
{
|
||||
char buffer[1024];
|
||||
IO::MemoryStream stream(buffer, 1024, 0);
|
||||
|
||||
m_serializationSettings.m_keepDefaults = true;
|
||||
|
||||
Test1::TestClass dataToSave;
|
||||
dataToSave.Init();
|
||||
dataToSave.m_float = 10;
|
||||
dataToSave.m_string = "SaveObjectToStreamSuccess";
|
||||
|
||||
Outcome<void, AZStd::string> saveResult = JsonSerializationUtils::SaveObjectToStream(&dataToSave, stream, (Test1::TestClass*)nullptr, &m_serializationSettings);
|
||||
|
||||
EXPECT_TRUE(saveResult.IsSuccess());
|
||||
|
||||
Test1::TestClass loadedData;
|
||||
stream.Seek(0, IO::GenericStream::ST_SEEK_BEGIN);
|
||||
Outcome<void, AZStd::string> loadResult = JsonSerializationUtils::LoadObjectFromStream(loadedData, stream, &m_deserializationSettings);
|
||||
|
||||
EXPECT_TRUE(loadResult.IsSuccess());
|
||||
|
||||
EXPECT_TRUE(dataToSave == loadedData);
|
||||
}
|
||||
|
||||
TEST_F(JsonSerializationUtilsTests, SaveObjectToStream_Failed_NoSerializationContext)
|
||||
{
|
||||
char buffer[1024];
|
||||
IO::MemoryStream stream(buffer, 1024, 0);
|
||||
|
||||
m_serializationSettings.m_keepDefaults = true;
|
||||
|
||||
Test1::TestClass dataToSave;
|
||||
dataToSave.m_float = 10;
|
||||
|
||||
Outcome<void, AZStd::string> saveResult = JsonSerializationUtils::SaveObjectToStream(&dataToSave, stream);
|
||||
|
||||
EXPECT_TRUE(!saveResult.IsSuccess());
|
||||
}
|
||||
|
||||
TEST_F(JsonSerializationUtilsTests, WriteJson)
|
||||
{
|
||||
rapidjson::Document document;
|
||||
document.SetObject();
|
||||
document.AddMember("a", 1, document.GetAllocator());
|
||||
document.AddMember("b", 2, document.GetAllocator());
|
||||
document.AddMember("c", 3, document.GetAllocator());
|
||||
|
||||
const char* expectedJsonText =
|
||||
"{\n"
|
||||
" \"a\": 1,\n"
|
||||
" \"b\": 2,\n"
|
||||
" \"c\": 3\n"
|
||||
"}";
|
||||
|
||||
AZStd::string outString;
|
||||
AZ::Outcome<void, AZStd::string> result1 = JsonSerializationUtils::WriteJsonString(document, outString);
|
||||
EXPECT_TRUE(result1.IsSuccess());
|
||||
EXPECT_STREQ(expectedJsonText, outString.c_str());
|
||||
|
||||
AZStd::vector<char> outBuffer;
|
||||
AZ::IO::ByteContainerStream<AZStd::vector<char>> outStream{&outBuffer};
|
||||
AZ::Outcome<void, AZStd::string> result2 = JsonSerializationUtils::WriteJsonStream(document, outStream);
|
||||
EXPECT_TRUE(result2.IsSuccess());
|
||||
|
||||
outBuffer.push_back(0);
|
||||
EXPECT_STREQ(expectedJsonText, outBuffer.data());
|
||||
|
||||
// Unfortunately we can't unit test WriteJsonFile because core unit tests don't have access to the local file IO system.
|
||||
}
|
||||
|
||||
TEST_F(JsonSerializationUtilsTests, ReadJsonString)
|
||||
{
|
||||
const char* jsonText =
|
||||
R"(
|
||||
{
|
||||
"a": 1,
|
||||
"b": 2,
|
||||
"c": 3
|
||||
})";
|
||||
|
||||
AZ::Outcome<rapidjson::Document, AZStd::string> result = JsonSerializationUtils::ReadJsonString(jsonText);
|
||||
|
||||
EXPECT_TRUE(result.IsSuccess());
|
||||
EXPECT_TRUE(result.GetValue().IsObject());
|
||||
EXPECT_TRUE(result.GetValue().HasMember("a"));
|
||||
EXPECT_TRUE(result.GetValue().HasMember("b"));
|
||||
EXPECT_TRUE(result.GetValue().HasMember("c"));
|
||||
EXPECT_EQ(result.GetValue()["a"].GetInt(), 1);
|
||||
EXPECT_EQ(result.GetValue()["b"].GetInt(), 2);
|
||||
EXPECT_EQ(result.GetValue()["c"].GetInt(), 3);
|
||||
}
|
||||
|
||||
TEST_F(JsonSerializationUtilsTests, ReadJsonString_ErrorReportsLineNumber)
|
||||
{
|
||||
const char* jsonText =
|
||||
R"(
|
||||
{
|
||||
"a": "This line is missing a comma"
|
||||
"b": 2,
|
||||
"c": 3
|
||||
}
|
||||
)";
|
||||
|
||||
AZ::Outcome<rapidjson::Document, AZStd::string> result = JsonSerializationUtils::ReadJsonString(jsonText);
|
||||
|
||||
EXPECT_FALSE(result.IsSuccess());
|
||||
EXPECT_TRUE(result.GetError().find("JSON parse error at line 4:") == 0);
|
||||
}
|
||||
|
||||
TEST_F(JsonSerializationUtilsTests, LoadJsonStream)
|
||||
{
|
||||
const char* jsonText =
|
||||
R"(
|
||||
{
|
||||
"a": 1,
|
||||
"b": 2,
|
||||
"c": 3
|
||||
})";
|
||||
|
||||
IO::MemoryStream stream(jsonText, strlen(jsonText));
|
||||
|
||||
AZ::Outcome<rapidjson::Document, AZStd::string> result = JsonSerializationUtils::ReadJsonStream(stream);
|
||||
|
||||
EXPECT_TRUE(result.IsSuccess());
|
||||
EXPECT_TRUE(result.GetValue().IsObject());
|
||||
EXPECT_TRUE(result.GetValue().HasMember("a"));
|
||||
EXPECT_TRUE(result.GetValue().HasMember("b"));
|
||||
EXPECT_TRUE(result.GetValue().HasMember("c"));
|
||||
EXPECT_EQ(result.GetValue()["a"].GetInt(), 1);
|
||||
EXPECT_EQ(result.GetValue()["b"].GetInt(), 2);
|
||||
EXPECT_EQ(result.GetValue()["c"].GetInt(), 3);
|
||||
}
|
||||
|
||||
TEST_F(JsonSerializationUtilsTests, LoadJsonStream_ErrorReportsLineNumber)
|
||||
{
|
||||
const char* jsonText =
|
||||
R"(
|
||||
{
|
||||
"a": 1,
|
||||
"b": "This line is missing a comma"
|
||||
"c": 3
|
||||
}
|
||||
)";
|
||||
|
||||
IO::MemoryStream stream(jsonText, strlen(jsonText));
|
||||
|
||||
AZ::Outcome<rapidjson::Document, AZStd::string> result = JsonSerializationUtils::ReadJsonStream(stream);
|
||||
|
||||
EXPECT_FALSE(result.IsSuccess());
|
||||
EXPECT_TRUE(result.GetError().find("JSON parse error at line 5:") == 0);
|
||||
}
|
||||
|
||||
TEST_F(JsonSerializationUtilsTests, LoadObjectFromStream_Failed_ParseError)
|
||||
{
|
||||
char buffer[1024] = "Not a Json";
|
||||
IO::MemoryStream stream(buffer, 1024);
|
||||
|
||||
Test1::TestClass dataToLoad;
|
||||
|
||||
Outcome<void, AZStd::string> loadResult = JsonSerializationUtils::LoadObjectFromStream(dataToLoad, stream, &m_deserializationSettings);
|
||||
|
||||
EXPECT_TRUE(!loadResult.IsSuccess());
|
||||
}
|
||||
|
||||
TEST_F(JsonSerializationUtilsTests, LoadObjectFromStream_Failed_NotJsonSerialization)
|
||||
{
|
||||
char buffer[1024] = "{}";
|
||||
IO::MemoryStream stream(buffer, 1024);
|
||||
|
||||
Test1::TestClass dataToLoad;
|
||||
|
||||
Outcome<void, AZStd::string> loadResult = JsonSerializationUtils::LoadObjectFromStream(dataToLoad, stream, &m_deserializationSettings);
|
||||
|
||||
EXPECT_TRUE(!loadResult.IsSuccess());
|
||||
}
|
||||
|
||||
TEST_F(JsonSerializationUtilsTests, LoadObjectFromStream_Failed_NoClassInfo)
|
||||
{
|
||||
char buffer[1024] =
|
||||
"{ "
|
||||
" \"Type\": \"JsonSerialization\" "
|
||||
"} ";
|
||||
|
||||
IO::MemoryStream stream(buffer, 1024);
|
||||
|
||||
Test1::TestClass dataToLoad;
|
||||
|
||||
Outcome<void, AZStd::string> loadResult = JsonSerializationUtils::LoadObjectFromStream(dataToLoad, stream, &m_deserializationSettings);
|
||||
|
||||
EXPECT_TRUE(!loadResult.IsSuccess());
|
||||
}
|
||||
|
||||
TEST_F(JsonSerializationUtilsTests, LoadObjectFromStream_Failed_MismatchClassName)
|
||||
{
|
||||
char buffer[1024] =
|
||||
"{ "
|
||||
" \"Type\": \"JsonSerialization\", "
|
||||
" \"ClassName\": \"NotTestClass\", "
|
||||
" \"ClassData\" : {} "
|
||||
"} ";
|
||||
IO::MemoryStream stream(buffer, 1024);
|
||||
|
||||
Test1::TestClass dataToLoad;
|
||||
|
||||
Outcome<void, AZStd::string> loadResult = JsonSerializationUtils::LoadObjectFromStream(dataToLoad, stream, &m_deserializationSettings);
|
||||
|
||||
EXPECT_TRUE(!loadResult.IsSuccess());
|
||||
}
|
||||
|
||||
TEST_F(JsonSerializationUtilsTests, LoadObjectFromStream_Failed_NoSerializeContext)
|
||||
{
|
||||
char buffer[1024] =
|
||||
"{ "
|
||||
" \"Type\": \"JsonSerialization\", "
|
||||
" \"ClassName\": \"TestClass\", "
|
||||
" \"ClassData\" : {} "
|
||||
"} ";
|
||||
IO::MemoryStream stream(buffer, 1024);
|
||||
|
||||
Test1::TestClass dataToLoad;
|
||||
|
||||
Outcome<void, AZStd::string> loadResult = JsonSerializationUtils::LoadObjectFromStream(dataToLoad, stream);
|
||||
|
||||
EXPECT_TRUE(!loadResult.IsSuccess());
|
||||
}
|
||||
|
||||
|
||||
TEST_F(JsonSerializationUtilsTests, LoadObjectFromStream_Failed_HaltMismatchClassMember)
|
||||
{
|
||||
char buffer[1024] =
|
||||
"{ "
|
||||
" \"Type\": \"JsonSerialization\", "
|
||||
" \"ClassName\": \"TestClass\", "
|
||||
" \"ClassData\" : { \"uint\":\"10\", "
|
||||
" \"bad name2\":\"blabla\"} "
|
||||
"} ";
|
||||
IO::MemoryStream stream(buffer, 1024);
|
||||
|
||||
Test1::TestClass dataToLoad;
|
||||
|
||||
Outcome<void, AZStd::string> loadResult = JsonSerializationUtils::LoadObjectFromStream(dataToLoad, stream, &m_deserializationSettings);
|
||||
|
||||
EXPECT_TRUE(!loadResult.IsSuccess());
|
||||
}
|
||||
|
||||
TEST_F(JsonSerializationUtilsTests, LoadObjectFromStream_Failed_WrongValueType)
|
||||
{
|
||||
char buffer[1024] =
|
||||
"{ "
|
||||
" \"Type\": \"JsonSerialization\", "
|
||||
" \"ClassName\": \"TestClass\", "
|
||||
" \"ClassData\" : { \"int\":\"Ten\"} "
|
||||
"} ";
|
||||
IO::MemoryStream stream(buffer, 1024);
|
||||
|
||||
Test1::TestClass dataToLoad;
|
||||
|
||||
Outcome<void, AZStd::string> loadResult = JsonSerializationUtils::LoadObjectFromStream(dataToLoad, stream, &m_deserializationSettings);
|
||||
|
||||
EXPECT_TRUE(!loadResult.IsSuccess());
|
||||
}
|
||||
|
||||
TEST_F(JsonSerializationUtilsTests, LoadObjectFromStream_Success_LessField)
|
||||
{
|
||||
char buffer[1024] =
|
||||
"{ "
|
||||
" \"Type\": \"JsonSerialization\", "
|
||||
" \"ClassName\": \"TestClass\", "
|
||||
" \"ClassData\" : { \"int\":\"10\"} "
|
||||
"} ";
|
||||
IO::MemoryStream stream(buffer, 1024);
|
||||
|
||||
Test1::TestClass dataToLoad;
|
||||
|
||||
Outcome<void, AZStd::string> loadResult = JsonSerializationUtils::LoadObjectFromStream(dataToLoad, stream, &m_deserializationSettings);
|
||||
|
||||
EXPECT_TRUE(loadResult.IsSuccess());
|
||||
EXPECT_TRUE(dataToLoad.m_int == 10);
|
||||
}
|
||||
|
||||
TEST_F(JsonSerializationUtilsTests, LoadObjectFromStream_Success_CustomizeCallback)
|
||||
{
|
||||
char buffer[1024] =
|
||||
"{ "
|
||||
" \"Type\": \"JsonSerialization\", "
|
||||
" \"ClassName\": \"TestClass\", "
|
||||
" \"ClassData\" : { \"int\":\"10\"} "
|
||||
"} ";
|
||||
IO::MemoryStream stream(buffer, 1024);
|
||||
|
||||
Test1::TestClass dataToLoad;
|
||||
|
||||
AZStd::string callbackString;
|
||||
auto issueReportingCallback = [&callbackString](AZStd::string_view message, JsonSerializationResult::ResultCode result, AZStd::string_view target) -> JsonSerializationResult::ResultCode
|
||||
{
|
||||
using namespace JsonSerializationResult;
|
||||
AZ_UNUSED(message);
|
||||
AZ_UNUSED(target);
|
||||
callbackString = "issueReportingCallback";
|
||||
return result;
|
||||
};
|
||||
|
||||
auto settings = m_deserializationSettings;
|
||||
settings.m_reporting = issueReportingCallback;
|
||||
Outcome<void, AZStd::string> loadResult = JsonSerializationUtils::LoadObjectFromStream(dataToLoad, stream, &settings);
|
||||
|
||||
EXPECT_TRUE(loadResult.IsSuccess());
|
||||
EXPECT_TRUE(dataToLoad.m_int == 10);
|
||||
EXPECT_TRUE(!callbackString.empty());
|
||||
}
|
||||
|
||||
TEST_F(JsonSerializationUtilsTests, LoadAnyObjectFromStream_Success)
|
||||
{
|
||||
char buffer[1024] =
|
||||
"{ "
|
||||
" \"Type\": \"JsonSerialization\", "
|
||||
" \"ClassName\": \"TestClass\", "
|
||||
" \"ClassData\" : { \"int\":\"10\"} "
|
||||
"} ";
|
||||
IO::MemoryStream stream(buffer, 1024);
|
||||
|
||||
Outcome<AZStd::any, AZStd::string> loadResult = JsonSerializationUtils::LoadAnyObjectFromStream(stream, &m_deserializationSettings);
|
||||
|
||||
EXPECT_TRUE(loadResult.IsSuccess());
|
||||
EXPECT_TRUE(loadResult.GetValue().type() == Test1::TestClass::TYPEINFO_Uuid());
|
||||
|
||||
Test1::TestClass test = AZStd::any_cast<Test1::TestClass>(loadResult.GetValue());
|
||||
EXPECT_TRUE(test.m_int == 10);
|
||||
}
|
||||
|
||||
TEST_F(JsonSerializationUtilsTests, LoadAnyObjectFromStream_Failed_WrongValueType)
|
||||
{
|
||||
char buffer[1024] =
|
||||
"{ "
|
||||
" \"Type\": \"JsonSerialization\", "
|
||||
" \"ClassName\": \"TestClass\", "
|
||||
" \"ClassData\" : { \"int\":\"Ten\"} "
|
||||
"} ";
|
||||
IO::MemoryStream stream(buffer, 1024);
|
||||
|
||||
Outcome<AZStd::any, AZStd::string> loadResult = JsonSerializationUtils::LoadAnyObjectFromStream(stream, &m_deserializationSettings);
|
||||
|
||||
EXPECT_TRUE(!loadResult.IsSuccess());
|
||||
}
|
||||
|
||||
|
||||
} // namespace UnitTest
|
||||
|
||||
@@ -104,6 +104,7 @@ set(FILES
|
||||
Serialization/Json/JsonSerializationResultTests.cpp
|
||||
Serialization/Json/JsonSerializationTests.h
|
||||
Serialization/Json/JsonSerializationTests.cpp
|
||||
Serialization/Json/JsonSerializationUtilsTests.cpp
|
||||
Serialization/Json/JsonSerializerConformityTests.h
|
||||
Serialization/Json/JsonSerializerMock.h
|
||||
Serialization/Json/MapSerializerTests.cpp
|
||||
|
||||
Reference in New Issue
Block a user