Merge pull request #6318 from aws-lumberyard-dev/PathSerializationSupport
Introduced a Json Serializer for the AzCore mutable path classes
This commit is contained in:
@@ -484,6 +484,7 @@ namespace AZ::IO
|
||||
// as_posix
|
||||
//! Replicates the behavior of the Python pathlib as_posix method
|
||||
//! by replacing the Windows Path Separator with the Posix Path Seperator
|
||||
constexpr string_type AsPosix() const;
|
||||
AZStd::string StringAsPosix() const;
|
||||
constexpr AZStd::fixed_string<MaxPathLength> FixedMaxPathStringAsPosix() const noexcept;
|
||||
|
||||
|
||||
@@ -1043,6 +1043,13 @@ namespace AZ::IO
|
||||
// as_posix
|
||||
// Returns a copy of the path with the path separators converted to PosixPathSeparator
|
||||
template <typename StringType>
|
||||
constexpr auto BasicPath<StringType>::AsPosix() const -> string_type
|
||||
{
|
||||
string_type resultPath(m_path.begin(), m_path.end());
|
||||
AZStd::replace(resultPath.begin(), resultPath.end(), WindowsPathSeparator, PosixPathSeparator);
|
||||
return resultPath;
|
||||
}
|
||||
template <typename StringType>
|
||||
AZStd::string BasicPath<StringType>::StringAsPosix() const
|
||||
{
|
||||
AZStd::string resultPath(m_path.begin(), m_path.end());
|
||||
|
||||
@@ -7,6 +7,8 @@
|
||||
*/
|
||||
|
||||
#include <AzCore/IO/Path/Path.h>
|
||||
#include <AzCore/Serialization/Json/RegistrationContext.h>
|
||||
#include <AzCore/Serialization/Json/PathSerializer.h>
|
||||
#include <AzCore/Serialization/SerializeContext.h>
|
||||
#include <AzCore/std/functional.h>
|
||||
|
||||
@@ -35,10 +37,8 @@ namespace AZ::IO
|
||||
size_t Save(const void* classPtr, IO::GenericStream& stream, bool) override
|
||||
{
|
||||
/// Save paths out using the PosixPathSeparator
|
||||
PathType path(reinterpret_cast<const PathType*>(classPtr)->Native(), AZ::IO::PosixPathSeparator);
|
||||
path.MakePreferred();
|
||||
|
||||
return static_cast<size_t>(stream.Write(path.Native().size(), path.c_str()));
|
||||
auto posixPathString{ reinterpret_cast<const PathType*>(classPtr)->AsPosix() };
|
||||
return static_cast<size_t>(stream.Write(posixPathString.size(), posixPathString.c_str()));
|
||||
}
|
||||
|
||||
bool Load(void* classPtr, IO::GenericStream& stream, unsigned int, bool) override
|
||||
@@ -73,5 +73,11 @@ namespace AZ::IO
|
||||
AZ::SerializeContext::IDataSerializer::CreateDefaultDeleteDeleter() })
|
||||
;
|
||||
}
|
||||
else if (auto jsonContext = azrtti_cast<JsonRegistrationContext*>(context))
|
||||
{
|
||||
jsonContext->Serializer<JsonPathSerializer>()
|
||||
->HandlesType<Path>()
|
||||
->HandlesType<FixedMaxPath>();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,127 @@
|
||||
/*
|
||||
* 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/Casting/numeric_cast.h>
|
||||
#include <AzCore/IO/Path/Path.h>
|
||||
#include <AzCore/Serialization/Json/JsonSerialization.h>
|
||||
#include <AzCore/Serialization/Json/StackedString.h>
|
||||
#include <AzCore/Serialization/Json/PathSerializer.h>
|
||||
#include <AzCore/std/containers/array.h>
|
||||
#include <AzCore/Memory/SystemAllocator.h>
|
||||
|
||||
namespace AZ::JsonPathSerializerInternal
|
||||
{
|
||||
template<typename PathType>
|
||||
static JsonSerializationResult::Result Load(PathType* pathValue, const rapidjson::Value& inputValue,
|
||||
JsonDeserializerContext& context)
|
||||
{
|
||||
namespace JSR = JsonSerializationResult; // Used remove name conflicts in AzCore in uber builds.
|
||||
|
||||
AZ_Assert(pathValue, "Expected a valid pointer to load from json value.");
|
||||
|
||||
switch (inputValue.GetType())
|
||||
{
|
||||
case rapidjson::kArrayType:
|
||||
case rapidjson::kObjectType:
|
||||
case rapidjson::kFalseType:
|
||||
case rapidjson::kTrueType:
|
||||
case rapidjson::kNumberType:
|
||||
[[fallthrough]];
|
||||
case rapidjson::kNullType:
|
||||
return context.Report(JSR::Tasks::ReadField, JSR::Outcomes::Unsupported,
|
||||
"Unsupported type. String values can't be read from arrays, objects or null.");
|
||||
case rapidjson::kStringType:
|
||||
{
|
||||
size_t pathLength = inputValue.GetStringLength();
|
||||
if (pathLength <= pathValue->Native().max_size())
|
||||
{
|
||||
*pathValue = PathType(AZStd::string_view(inputValue.GetString(), pathLength)).LexicallyNormal();
|
||||
return context.Report(JSR::Tasks::ReadField, JSR::Outcomes::Success, "Successfully read path.");
|
||||
}
|
||||
using UuidString = AZStd::fixed_string<AZ::Uuid::MaxStringBuffer>;
|
||||
using ErrorString = AZStd::fixed_string<256>;
|
||||
return context.Report(JsonSerializationResult::Tasks::ReadField, JSR::Outcomes::Invalid,
|
||||
ErrorString::format("Json string value is too large to fit within path type %s. It needs to be less than %zu code points",
|
||||
azrtti_typeid<PathType>().template ToString<UuidString>().c_str(), pathValue->Native().max_size()));
|
||||
}
|
||||
default:
|
||||
return context.Report(JSR::Tasks::ReadField, JSR::Outcomes::Unknown, "Unknown json type encountered for string value.");
|
||||
}
|
||||
}
|
||||
template<typename PathType>
|
||||
static JsonSerializationResult::Result StoreWithDefault(rapidjson::Value& outputValue, const PathType* pathValue,
|
||||
const PathType* defaultPathValue, JsonSerializerContext& context)
|
||||
{
|
||||
namespace JSR = JsonSerializationResult; // Removes name conflicts in AzCore in uber builds.
|
||||
|
||||
if (context.ShouldKeepDefaults() || defaultPathValue == nullptr || *pathValue != *defaultPathValue)
|
||||
{
|
||||
auto posixPathString = pathValue->AsPosix();
|
||||
outputValue.SetString(posixPathString.c_str(), aznumeric_caster(posixPathString.size()), context.GetJsonAllocator());
|
||||
return context.Report(JSR::Tasks::WriteValue, JSR::Outcomes::Success, "Path successfully stored.");
|
||||
}
|
||||
|
||||
return context.Report(JSR::Tasks::WriteValue, JSR::Outcomes::DefaultsUsed, "Default Path used.");
|
||||
}
|
||||
}
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
AZ_CLASS_ALLOCATOR_IMPL(JsonPathSerializer, SystemAllocator, 0);
|
||||
|
||||
JsonSerializationResult::Result JsonPathSerializer::Load(void* outputValue, const Uuid& outputValueTypeId,
|
||||
const rapidjson::Value& inputValue, JsonDeserializerContext& context)
|
||||
{
|
||||
if (outputValueTypeId == azrtti_typeid<AZ::IO::Path>())
|
||||
{
|
||||
return JsonPathSerializerInternal::Load(reinterpret_cast<AZ::IO::Path*>(outputValue), inputValue,
|
||||
context);
|
||||
}
|
||||
else if (outputValueTypeId == azrtti_typeid<AZ::IO::FixedMaxPath>())
|
||||
{
|
||||
return JsonPathSerializerInternal::Load(reinterpret_cast<AZ::IO::FixedMaxPath*>(outputValue), inputValue,
|
||||
context);
|
||||
}
|
||||
|
||||
using UuidString = AZStd::fixed_string<AZ::Uuid::MaxStringBuffer>;
|
||||
auto errorTypeIdString = outputValueTypeId.ToString<UuidString>();
|
||||
AZ_Assert(false, "Unable to serialize json string"
|
||||
" to a path of type %s", errorTypeIdString.c_str());
|
||||
|
||||
using ErrorString = AZStd::fixed_string<256>;
|
||||
return context.Report(JsonSerializationResult::Tasks::ReadField, JsonSerializationResult::Outcomes::TypeMismatch,
|
||||
ErrorString::format("Output value type ID %s is not a valid Path type", errorTypeIdString.c_str()));
|
||||
}
|
||||
|
||||
JsonSerializationResult::Result JsonPathSerializer::Store(rapidjson::Value& outputValue, const void* inputValue,
|
||||
const void* defaultValue, const Uuid& valueTypeId, JsonSerializerContext& context)
|
||||
{
|
||||
if (valueTypeId == azrtti_typeid<AZ::IO::Path>())
|
||||
{
|
||||
return JsonPathSerializerInternal::StoreWithDefault(outputValue,
|
||||
reinterpret_cast<const AZ::IO::Path*>(inputValue),
|
||||
reinterpret_cast<const AZ::IO::Path*>(defaultValue), context);
|
||||
}
|
||||
else if (valueTypeId == azrtti_typeid<AZ::IO::FixedMaxPath>())
|
||||
{
|
||||
return JsonPathSerializerInternal::StoreWithDefault(outputValue,
|
||||
reinterpret_cast<const AZ::IO::FixedMaxPath*>(inputValue),
|
||||
reinterpret_cast<const AZ::IO::FixedMaxPath*>(defaultValue), context);
|
||||
}
|
||||
|
||||
using UuidString = AZStd::fixed_string<AZ::Uuid::MaxStringBuffer>;
|
||||
auto errorTypeIdString = valueTypeId.ToString<UuidString>();
|
||||
AZ_Assert(false, "Unable to serialize path type %s to a json string",
|
||||
errorTypeIdString.c_str());
|
||||
|
||||
using ErrorString = AZStd::fixed_string<256>;
|
||||
return context.Report(JsonSerializationResult::Tasks::WriteValue, JsonSerializationResult::Outcomes::TypeMismatch,
|
||||
ErrorString::format("Input value type ID %s is not a valid Path type", errorTypeIdString.c_str()));
|
||||
}
|
||||
|
||||
} // namespace AZ
|
||||
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
* 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/Json/BaseJsonSerializer.h>
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
class JsonPathSerializer
|
||||
: public BaseJsonSerializer
|
||||
{
|
||||
public:
|
||||
AZ_RTTI(JsonPathSerializer, "{F6FBA901-07E0-4F03-A0B6-72A9A6CE1E96}", 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;
|
||||
};
|
||||
} // namespace AZ
|
||||
@@ -533,6 +533,8 @@ set(FILES
|
||||
Serialization/Json/JsonUtils.cpp
|
||||
Serialization/Json/MapSerializer.h
|
||||
Serialization/Json/MapSerializer.cpp
|
||||
Serialization/Json/PathSerializer.h
|
||||
Serialization/Json/PathSerializer.cpp
|
||||
Serialization/Json/RegistrationContext.h
|
||||
Serialization/Json/RegistrationContext.cpp
|
||||
Serialization/Json/SmartPointerSerializer.h
|
||||
|
||||
@@ -0,0 +1,106 @@
|
||||
/*
|
||||
* 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/IO/Path/Path.h>
|
||||
#include <AzCore/IO/Path/PathReflect.h>
|
||||
#include <AzCore/Serialization/Json/PathSerializer.h>
|
||||
#include <Tests/Serialization/Json/BaseJsonSerializerFixture.h>
|
||||
#include <Tests/Serialization/Json/JsonSerializerConformityTests.h>
|
||||
|
||||
namespace JsonSerializationTests
|
||||
{
|
||||
template<typename PathType>
|
||||
class PathTestDescription
|
||||
: public JsonSerializerConformityTestDescriptor<PathType>
|
||||
{
|
||||
public:
|
||||
using JsonSerializerConformityTestDescriptor<PathType>::Reflect;
|
||||
void Reflect(AZStd::unique_ptr<AZ::SerializeContext>& serializeContext) override
|
||||
{
|
||||
AZ::IO::PathReflect(serializeContext.get());
|
||||
}
|
||||
void Reflect(AZStd::unique_ptr<AZ::JsonRegistrationContext>& jsonContext) override
|
||||
{
|
||||
AZ::IO::PathReflect(jsonContext.get());
|
||||
}
|
||||
AZStd::shared_ptr<AZ::BaseJsonSerializer> CreateSerializer() override
|
||||
{
|
||||
return AZStd::make_shared<AZ::JsonPathSerializer>();
|
||||
}
|
||||
|
||||
AZStd::shared_ptr<PathType> CreateDefaultInstance() override
|
||||
{
|
||||
return AZStd::make_shared<PathType>();
|
||||
}
|
||||
|
||||
AZStd::shared_ptr<PathType> CreateFullySetInstance() override
|
||||
{
|
||||
return AZStd::make_shared<PathType>("O3DE/Relative/Path");
|
||||
}
|
||||
|
||||
AZStd::string_view GetJsonForFullySetInstance() override
|
||||
{
|
||||
return R"("O3DE/Relative/Path")";
|
||||
}
|
||||
|
||||
void ConfigureFeatures(JsonSerializerConformityTestDescriptorFeatures& features) override
|
||||
{
|
||||
features.EnableJsonType(rapidjson::kStringType);
|
||||
features.m_supportsPartialInitialization = false;
|
||||
features.m_supportsInjection = false;
|
||||
}
|
||||
|
||||
bool AreEqual(const PathType& lhs, const PathType& rhs) override
|
||||
{
|
||||
return lhs == rhs;
|
||||
}
|
||||
};
|
||||
|
||||
using PathConformityTestTypes = ::testing::Types<
|
||||
PathTestDescription<AZ::IO::Path>,
|
||||
PathTestDescription<AZ::IO::FixedMaxPath>
|
||||
>;
|
||||
INSTANTIATE_TYPED_TEST_CASE_P(Path, JsonSerializerConformityTests, PathConformityTestTypes);
|
||||
|
||||
|
||||
class PathSerializerTests
|
||||
: public BaseJsonSerializerFixture
|
||||
{
|
||||
public:
|
||||
AZStd::unique_ptr<AZ::JsonPathSerializer> m_serializer;
|
||||
|
||||
void SetUp() override
|
||||
{
|
||||
BaseJsonSerializerFixture::SetUp();
|
||||
m_serializer = AZStd::make_unique<AZ::JsonPathSerializer>();
|
||||
}
|
||||
|
||||
void TearDown() override
|
||||
{
|
||||
m_serializer.reset();
|
||||
BaseJsonSerializerFixture::TearDown();
|
||||
}
|
||||
};
|
||||
|
||||
TEST_F(PathSerializerTests, LoadingIntoFixedMaxPath_GreaterThanMaxPathLength_Fails)
|
||||
{
|
||||
AZ::IO::Path testPath;
|
||||
// Fill a path greater than the AZ::IO::MaxPathLength in write it to Json
|
||||
testPath.Native().append(AZ::IO::MaxPathLength + 2, 'a');
|
||||
|
||||
rapidjson::Value loadPathValue;
|
||||
AZ::JsonSerializationResult::ResultCode resultCode = m_serializer->Store(loadPathValue,
|
||||
&testPath, nullptr, azrtti_typeid<AZ::IO::Path>(), *m_jsonSerializationContext);
|
||||
EXPECT_EQ(AZ::JsonSerializationResult::Outcomes::Success, resultCode.GetOutcome());
|
||||
|
||||
AZ::IO::FixedMaxPath resultPath;
|
||||
AZ::JsonSerializationResult::ResultCode result = m_serializer->Load(&resultPath, azrtti_typeid<AZ::IO::FixedMaxPath>(),
|
||||
loadPathValue, *m_jsonDeserializationContext);
|
||||
EXPECT_GE(result.GetOutcome(), AZ::JsonSerializationResult::Outcomes::Invalid);
|
||||
}
|
||||
} // namespace JsonSerializationTests
|
||||
@@ -111,6 +111,7 @@ set(FILES
|
||||
Serialization/Json/MapSerializerTests.cpp
|
||||
Serialization/Json/MathVectorSerializerTests.cpp
|
||||
Serialization/Json/MathMatrixSerializerTests.cpp
|
||||
Serialization/Json/PathSerializerTests.cpp
|
||||
Serialization/Json/SmartPointerSerializerTests.cpp
|
||||
Serialization/Json/StringSerializerTests.cpp
|
||||
Serialization/Json/TestCases.h
|
||||
|
||||
Reference in New Issue
Block a user