API tweaks
- Use ref types - Support using rapidjson::Value in lieu of rapidjson::Document - Use the existing JSON comparison util function in tests Signed-off-by: Nicholas Van Sickle <nvsickle@amazon.com>
This commit is contained in:
@@ -10,17 +10,17 @@
|
||||
|
||||
namespace AZ::Dom
|
||||
{
|
||||
Visitor::Result JsonBackend::ReadFromStringInPlace(AZStd::string& buffer, Visitor* visitor)
|
||||
Visitor::Result JsonBackend::ReadFromStringInPlace(AZStd::string& buffer, Visitor& visitor)
|
||||
{
|
||||
return Json::VisitSerializedJsonInPlace(buffer, visitor);
|
||||
}
|
||||
|
||||
Visitor::Result JsonBackend::ReadFromString(AZStd::string_view buffer, Lifetime lifetime, Visitor* visitor)
|
||||
Visitor::Result JsonBackend::ReadFromString(AZStd::string_view buffer, Lifetime lifetime, Visitor& visitor)
|
||||
{
|
||||
return Json::VisitSerializedJson(buffer, lifetime, visitor);
|
||||
}
|
||||
|
||||
AZStd::unique_ptr<Visitor> JsonBackend::CreateStreamWriter(AZ::IO::GenericStream* stream)
|
||||
AZStd::unique_ptr<Visitor> JsonBackend::CreateStreamWriter(AZ::IO::GenericStream& stream)
|
||||
{
|
||||
return Json::GetJsonStreamWriter(stream, Json::OutputFormatting::PrettyPrintedJson);
|
||||
}
|
||||
|
||||
@@ -16,8 +16,8 @@ namespace AZ::Dom
|
||||
class JsonBackend final : public Backend
|
||||
{
|
||||
public:
|
||||
Visitor::Result ReadFromStringInPlace(AZStd::string& buffer, Visitor* visitor) override;
|
||||
Visitor::Result ReadFromString(AZStd::string_view buffer, Lifetime lifetime, Visitor* visitor) override;
|
||||
AZStd::unique_ptr<Visitor> CreateStreamWriter(AZ::IO::GenericStream* stream) override;
|
||||
Visitor::Result ReadFromStringInPlace(AZStd::string& buffer, Visitor& visitor) override;
|
||||
Visitor::Result ReadFromString(AZStd::string_view buffer, Lifetime lifetime, Visitor& visitor) override;
|
||||
AZStd::unique_ptr<Visitor> CreateStreamWriter(AZ::IO::GenericStream& stream) override;
|
||||
};
|
||||
} // namespace AZ::Dom
|
||||
|
||||
@@ -16,7 +16,6 @@
|
||||
#include <AzCore/JSON/rapidjson.h>
|
||||
#include <AzCore/JSON/reader.h>
|
||||
#include <AzCore/JSON/writer.h>
|
||||
#include <AzCore/std/containers/queue.h>
|
||||
#include <AzCore/std/containers/stack.h>
|
||||
#include <AzCore/std/containers/variant.h>
|
||||
#include <AzCore/std/optional.h>
|
||||
@@ -24,202 +23,185 @@
|
||||
namespace AZ::Dom::Json
|
||||
{
|
||||
//
|
||||
// class DocumentWriter
|
||||
// class RapidJsonValueWriter
|
||||
//
|
||||
// Visitor that produces a rapidjson::Document
|
||||
class DocumentWriter final : public Visitor
|
||||
RapidJsonValueWriter::RapidJsonValueWriter(rapidjson::Value& outputValue, rapidjson::Value::AllocatorType& allocator)
|
||||
: m_result(outputValue)
|
||||
, m_allocator(allocator)
|
||||
{
|
||||
public:
|
||||
VisitorFlags GetVisitorFlags() const override
|
||||
}
|
||||
|
||||
VisitorFlags RapidJsonValueWriter::GetVisitorFlags() const
|
||||
{
|
||||
return VisitorFlags::SupportsRawKeys | VisitorFlags::SupportsArrays | VisitorFlags::SupportsObjects;
|
||||
}
|
||||
|
||||
Visitor::Result RapidJsonValueWriter::Null()
|
||||
{
|
||||
CurrentValue().SetNull();
|
||||
return FinishWrite();
|
||||
}
|
||||
|
||||
Visitor::Result RapidJsonValueWriter::Bool(bool value)
|
||||
{
|
||||
CurrentValue().SetBool(value);
|
||||
return FinishWrite();
|
||||
}
|
||||
|
||||
Visitor::Result RapidJsonValueWriter::Int64(AZ::s64 value)
|
||||
{
|
||||
CurrentValue().SetInt64(value);
|
||||
return FinishWrite();
|
||||
}
|
||||
|
||||
Visitor::Result RapidJsonValueWriter::Uint64(AZ::u64 value)
|
||||
{
|
||||
CurrentValue().SetUint64(value);
|
||||
return FinishWrite();
|
||||
}
|
||||
|
||||
Visitor::Result RapidJsonValueWriter::Double(double value)
|
||||
{
|
||||
CurrentValue().SetDouble(value);
|
||||
return FinishWrite();
|
||||
}
|
||||
|
||||
Visitor::Result RapidJsonValueWriter::String(AZStd::string_view value, Lifetime lifetime)
|
||||
{
|
||||
if (lifetime == Lifetime::Temporary)
|
||||
{
|
||||
return VisitorFlags::SupportsRawKeys | VisitorFlags::SupportsArrays | VisitorFlags::SupportsObjects;
|
||||
CurrentValue().SetString(value.data(), static_cast<rapidjson::SizeType>(value.length()), m_allocator);
|
||||
}
|
||||
else
|
||||
{
|
||||
CurrentValue().SetString(value.data(), static_cast<rapidjson::SizeType>(value.length()));
|
||||
}
|
||||
return FinishWrite();
|
||||
}
|
||||
|
||||
Visitor::Result RapidJsonValueWriter::StartObject()
|
||||
{
|
||||
CurrentValue().SetObject();
|
||||
|
||||
const bool isObject = true;
|
||||
m_entryStack.emplace_front(isObject, CurrentValue());
|
||||
return VisitorSuccess();
|
||||
}
|
||||
|
||||
Visitor::Result RapidJsonValueWriter::EndObject(AZ::u64 attributeCount)
|
||||
{
|
||||
if (m_entryStack.empty())
|
||||
{
|
||||
return VisitorFailure(VisitorErrorCode::InternalError, "EndObject called without a matching BeginObject call");
|
||||
}
|
||||
|
||||
Result Null() override
|
||||
if (!m_entryStack.front().m_isObject)
|
||||
{
|
||||
CurrentValue().SetNull();
|
||||
return FinishWrite();
|
||||
return VisitorFailure(VisitorErrorCode::InternalError, "Expected EndArray and received EndObject instead");
|
||||
}
|
||||
|
||||
Result Bool(bool value) override
|
||||
if (m_entryStack.front().m_entryCount != attributeCount)
|
||||
{
|
||||
CurrentValue().SetBool(value);
|
||||
return FinishWrite();
|
||||
return FormatVisitorFailure(
|
||||
VisitorErrorCode::InternalError, "EndObject: Expected %lu attributes but received %lu attributes instead", attributeCount,
|
||||
m_entryStack.front().m_entryCount);
|
||||
}
|
||||
|
||||
Result Int64(AZ::s64 value) override
|
||||
m_entryStack.pop_front();
|
||||
return FinishWrite();
|
||||
}
|
||||
|
||||
Visitor::Result RapidJsonValueWriter::Key(AZ::Name key)
|
||||
{
|
||||
return RawKey(key.GetStringView(), Lifetime::Persistent);
|
||||
}
|
||||
|
||||
Visitor::Result RapidJsonValueWriter::RawKey(AZStd::string_view key, Lifetime lifetime)
|
||||
{
|
||||
AZ_Assert(!m_entryStack.empty(), "Attempmted to push a key with no object");
|
||||
AZ_Assert(m_entryStack.front().m_isObject, "Attempted to push a key to an array");
|
||||
if (lifetime == Lifetime::Persistent)
|
||||
{
|
||||
CurrentValue().SetInt64(value);
|
||||
return FinishWrite();
|
||||
m_entryStack.front().m_key.SetString(key.data(), static_cast<rapidjson::SizeType>(key.size()));
|
||||
}
|
||||
else
|
||||
{
|
||||
m_entryStack.front().m_key.SetString(key.data(), static_cast<rapidjson::SizeType>(key.size()), m_allocator);
|
||||
}
|
||||
return VisitorSuccess();
|
||||
}
|
||||
|
||||
Visitor::Result RapidJsonValueWriter::StartArray()
|
||||
{
|
||||
CurrentValue().SetArray();
|
||||
|
||||
const bool isObject = false;
|
||||
m_entryStack.emplace_front(isObject, CurrentValue());
|
||||
return VisitorSuccess();
|
||||
}
|
||||
|
||||
Visitor::Result RapidJsonValueWriter::EndArray(AZ::u64 elementCount)
|
||||
{
|
||||
if (m_entryStack.empty())
|
||||
{
|
||||
return VisitorFailure(VisitorErrorCode::InternalError, "EndArray called without a matching BeginArray call");
|
||||
}
|
||||
|
||||
Result Uint64(AZ::u64 value) override
|
||||
if (m_entryStack.front().m_isObject)
|
||||
{
|
||||
CurrentValue().SetUint64(value);
|
||||
return FinishWrite();
|
||||
return VisitorFailure(VisitorErrorCode::InternalError, "Expected EndObject and received EndArray instead");
|
||||
}
|
||||
|
||||
Result Double(double value) override
|
||||
if (m_entryStack.front().m_entryCount != elementCount)
|
||||
{
|
||||
CurrentValue().SetDouble(value);
|
||||
return FinishWrite();
|
||||
return FormatVisitorFailure(
|
||||
VisitorErrorCode::InternalError, "EndArray: Expected %lu elements but received %lu elements instead", elementCount,
|
||||
m_entryStack.front().m_entryCount);
|
||||
}
|
||||
|
||||
Result String(AZStd::string_view value, Lifetime lifetime) override
|
||||
{
|
||||
if (lifetime == Lifetime::Temporary)
|
||||
{
|
||||
CurrentValue().SetString(value.data(), static_cast<rapidjson::SizeType>(value.length()), m_result.GetAllocator());
|
||||
}
|
||||
else
|
||||
{
|
||||
CurrentValue().SetString(value.data(), static_cast<rapidjson::SizeType>(value.length()));
|
||||
}
|
||||
return FinishWrite();
|
||||
}
|
||||
m_entryStack.pop_front();
|
||||
return FinishWrite();
|
||||
}
|
||||
|
||||
Result StartObject() override
|
||||
Visitor::Result RapidJsonValueWriter::FinishWrite()
|
||||
{
|
||||
if (m_entryStack.empty())
|
||||
{
|
||||
CurrentValue().SetObject();
|
||||
|
||||
const bool isObject = true;
|
||||
m_entryStack.emplace_front(isObject, CurrentValue());
|
||||
return VisitorSuccess();
|
||||
}
|
||||
|
||||
Result EndObject(AZ::u64 attributeCount) override
|
||||
// Retrieve the top value of the stack and replace it with a null value
|
||||
rapidjson::Value value;
|
||||
m_entryStack.front().m_value.Swap(value);
|
||||
++m_entryStack.front().m_entryCount;
|
||||
|
||||
if (m_entryStack.front().m_key.IsString())
|
||||
{
|
||||
if (m_entryStack.empty())
|
||||
{
|
||||
return VisitorFailure(VisitorErrorCode::InternalError, "EndObject called without a matching BeginObject call");
|
||||
}
|
||||
|
||||
if (!m_entryStack.front().m_isObject)
|
||||
{
|
||||
return VisitorFailure(VisitorErrorCode::InternalError, "Expected EndArray and received EndObject instead");
|
||||
}
|
||||
|
||||
if (m_entryStack.front().m_entryCount != attributeCount)
|
||||
{
|
||||
return FormatVisitorFailure(
|
||||
VisitorErrorCode::InternalError, "EndObject: Expected %lu attributes but received %lu attributes instead",
|
||||
attributeCount, m_entryStack.front().m_entryCount);
|
||||
}
|
||||
|
||||
m_entryStack.pop_front();
|
||||
return FinishWrite();
|
||||
m_entryStack.front().m_container.AddMember(m_entryStack.front().m_key.Move(), AZStd::move(value), m_allocator);
|
||||
m_entryStack.front().m_key.SetNull();
|
||||
}
|
||||
else
|
||||
{
|
||||
m_entryStack.front().m_container.PushBack(AZStd::move(value), m_allocator);
|
||||
}
|
||||
|
||||
Result Key(AZ::Name key) override
|
||||
return VisitorSuccess();
|
||||
}
|
||||
|
||||
rapidjson::Value& RapidJsonValueWriter::CurrentValue()
|
||||
{
|
||||
if (m_entryStack.empty())
|
||||
{
|
||||
return RawKey(key.GetStringView(), Lifetime::Persistent);
|
||||
return m_result;
|
||||
}
|
||||
return m_entryStack.front().m_value;
|
||||
}
|
||||
|
||||
Result RawKey(AZStd::string_view key, Lifetime lifetime) override
|
||||
{
|
||||
AZ_Assert(!m_entryStack.empty(), "Attempmted to push a key with no object");
|
||||
AZ_Assert(m_entryStack.front().m_isObject, "Attempted to push a key to an array");
|
||||
if (lifetime == Lifetime::Persistent)
|
||||
{
|
||||
m_entryStack.front().m_key.SetString(key.data(), static_cast<rapidjson::SizeType>(key.size()));
|
||||
}
|
||||
else
|
||||
{
|
||||
m_entryStack.front().m_key.SetString(key.data(), static_cast<rapidjson::SizeType>(key.size()), m_result.GetAllocator());
|
||||
}
|
||||
return VisitorSuccess();
|
||||
}
|
||||
|
||||
Result StartArray() override
|
||||
{
|
||||
CurrentValue().SetArray();
|
||||
|
||||
const bool isObject = false;
|
||||
m_entryStack.emplace_front(isObject, CurrentValue());
|
||||
return VisitorSuccess();
|
||||
}
|
||||
|
||||
Result EndArray(AZ::u64 elementCount) override
|
||||
{
|
||||
if (m_entryStack.empty())
|
||||
{
|
||||
return VisitorFailure(VisitorErrorCode::InternalError, "EndArray called without a matching BeginArray call");
|
||||
}
|
||||
|
||||
if (m_entryStack.front().m_isObject)
|
||||
{
|
||||
return VisitorFailure(VisitorErrorCode::InternalError, "Expected EndObject and received EndArray instead");
|
||||
}
|
||||
|
||||
if (m_entryStack.front().m_entryCount != elementCount)
|
||||
{
|
||||
return FormatVisitorFailure(
|
||||
VisitorErrorCode::InternalError, "EndArray: Expected %lu elements but received %lu elements instead", elementCount,
|
||||
m_entryStack.front().m_entryCount);
|
||||
}
|
||||
|
||||
m_entryStack.pop_front();
|
||||
return FinishWrite();
|
||||
}
|
||||
|
||||
rapidjson::Document&& TakeDocument()
|
||||
{
|
||||
return AZStd::move(m_result);
|
||||
}
|
||||
|
||||
private:
|
||||
Result FinishWrite()
|
||||
{
|
||||
if (m_entryStack.empty())
|
||||
{
|
||||
return VisitorSuccess();
|
||||
}
|
||||
|
||||
// Retrieve the top value of the stack and replace it with a null value
|
||||
rapidjson::Value value;
|
||||
m_entryStack.front().m_value.Swap(value);
|
||||
++m_entryStack.front().m_entryCount;
|
||||
|
||||
if (m_entryStack.front().m_key.IsString())
|
||||
{
|
||||
m_entryStack.front().m_container.AddMember(m_entryStack.front().m_key.Move(), AZStd::move(value), m_result.GetAllocator());
|
||||
m_entryStack.front().m_key.SetNull();
|
||||
}
|
||||
else
|
||||
{
|
||||
m_entryStack.front().m_container.PushBack(AZStd::move(value), m_result.GetAllocator());
|
||||
}
|
||||
|
||||
return VisitorSuccess();
|
||||
}
|
||||
|
||||
rapidjson::Value& CurrentValue()
|
||||
{
|
||||
if (m_entryStack.empty())
|
||||
{
|
||||
return m_result;
|
||||
}
|
||||
return m_entryStack.front().m_value;
|
||||
}
|
||||
|
||||
struct ValueInfo
|
||||
{
|
||||
ValueInfo(bool isObject, rapidjson::Value& container)
|
||||
: m_isObject(isObject)
|
||||
, m_container(container)
|
||||
{
|
||||
}
|
||||
|
||||
rapidjson::Value m_key;
|
||||
rapidjson::Value m_value;
|
||||
rapidjson::Value& m_container;
|
||||
AZ::u64 m_entryCount = 0;
|
||||
bool m_isObject;
|
||||
};
|
||||
|
||||
rapidjson::Document m_result;
|
||||
AZStd::deque<ValueInfo> m_entryStack;
|
||||
};
|
||||
RapidJsonValueWriter::ValueInfo::ValueInfo(bool isObject, rapidjson::Value& container)
|
||||
: m_isObject(isObject)
|
||||
, m_container(container)
|
||||
{
|
||||
}
|
||||
|
||||
//
|
||||
// class StreamWriter
|
||||
@@ -503,36 +485,36 @@ namespace AZ::Dom::Json
|
||||
//
|
||||
// Serialized JSON util functions
|
||||
//
|
||||
AZStd::unique_ptr<Visitor> GetJsonStreamWriter(AZ::IO::GenericStream* stream, OutputFormatting format)
|
||||
AZStd::unique_ptr<Visitor> GetJsonStreamWriter(AZ::IO::GenericStream& stream, OutputFormatting format)
|
||||
{
|
||||
if (format == OutputFormatting::MinifiedJson)
|
||||
{
|
||||
using WriterType = rapidjson::Writer<AZ::IO::RapidJSONStreamWriter>;
|
||||
return AZStd::make_unique<StreamWriter<WriterType>>(stream);
|
||||
return AZStd::make_unique<StreamWriter<WriterType>>(&stream);
|
||||
}
|
||||
else
|
||||
{
|
||||
using WriterType = rapidjson::PrettyWriter<AZ::IO::RapidJSONStreamWriter>;
|
||||
return AZStd::make_unique<StreamWriter<WriterType>>(stream);
|
||||
return AZStd::make_unique<StreamWriter<WriterType>>(&stream);
|
||||
}
|
||||
}
|
||||
|
||||
Visitor::Result VisitSerializedJson(AZStd::string_view buffer, Lifetime lifetime, Visitor* visitor)
|
||||
Visitor::Result VisitSerializedJson(AZStd::string_view buffer, Lifetime lifetime, Visitor& visitor)
|
||||
{
|
||||
rapidjson::Reader reader;
|
||||
rapidjson::MemoryStream stream(buffer.data(), buffer.size());
|
||||
JsonReadHandler handler(visitor, lifetime);
|
||||
JsonReadHandler handler(&visitor, lifetime);
|
||||
|
||||
constexpr int flags = rapidjson::kParseCommentsFlag;
|
||||
reader.Parse<flags>(stream, handler);
|
||||
return handler.TakeOutcome();
|
||||
}
|
||||
|
||||
Visitor::Result VisitSerializedJsonInPlace(AZStd::string& buffer, Visitor* visitor)
|
||||
Visitor::Result VisitSerializedJsonInPlace(AZStd::string& buffer, Visitor& visitor)
|
||||
{
|
||||
rapidjson::Reader reader;
|
||||
AzStringStream stream(buffer);
|
||||
JsonReadHandler handler(visitor, Lifetime::Persistent);
|
||||
JsonReadHandler handler(&visitor, Lifetime::Persistent);
|
||||
|
||||
constexpr int flags = rapidjson::kParseCommentsFlag | rapidjson::kParseInsituFlag;
|
||||
reader.Parse<flags>(stream, handler);
|
||||
@@ -544,16 +526,24 @@ namespace AZ::Dom::Json
|
||||
//
|
||||
AZ::Outcome<rapidjson::Document, AZStd::string> WriteToRapidJsonDocument(Backend::WriteCallback writeCallback)
|
||||
{
|
||||
DocumentWriter writer;
|
||||
auto result = writeCallback(&writer);
|
||||
rapidjson::Document document;
|
||||
RapidJsonValueWriter writer(document, document.GetAllocator());
|
||||
auto result = writeCallback(writer);
|
||||
if (!result.IsSuccess())
|
||||
{
|
||||
return AZ::Failure(result.TakeError().FormatVisitorErrorMessage());
|
||||
}
|
||||
return AZ::Success(writer.TakeDocument());
|
||||
return AZ::Success(AZStd::move(document));
|
||||
}
|
||||
|
||||
Visitor::Result VisitRapidJsonValue(const rapidjson::Value& value, Visitor* visitor, Lifetime lifetime)
|
||||
Visitor::Result WriteToRapidJsonValue(
|
||||
rapidjson::Value& value, rapidjson::Value::AllocatorType& allocator, Backend::WriteCallback writeCallback)
|
||||
{
|
||||
RapidJsonValueWriter writer(value, allocator);
|
||||
return writeCallback(writer);
|
||||
}
|
||||
|
||||
Visitor::Result VisitRapidJsonValue(const rapidjson::Value& value, Visitor& visitor, Lifetime lifetime)
|
||||
{
|
||||
struct EndArrayMarker
|
||||
{
|
||||
@@ -579,7 +569,7 @@ namespace AZ::Dom::Json
|
||||
Visitor::Result result = AZ::Success();
|
||||
|
||||
AZStd::visit(
|
||||
[visitor, &entryStack, &entryCountStack, &result, lifetime](auto&& arg)
|
||||
[&visitor, &entryStack, &entryCountStack, &result, lifetime](auto&& arg)
|
||||
{
|
||||
using Alternative = AZStd::decay_t<decltype(arg)>;
|
||||
if constexpr (AZStd::is_same_v<Alternative, const rapidjson::Value*>)
|
||||
@@ -593,18 +583,18 @@ namespace AZ::Dom::Json
|
||||
switch (currentValue.GetType())
|
||||
{
|
||||
case rapidjson::kNullType:
|
||||
result = visitor->Null();
|
||||
result = visitor.Null();
|
||||
break;
|
||||
case rapidjson::kFalseType:
|
||||
result = visitor->Bool(false);
|
||||
result = visitor.Bool(false);
|
||||
break;
|
||||
case rapidjson::kTrueType:
|
||||
result = visitor->Bool(true);
|
||||
result = visitor.Bool(true);
|
||||
break;
|
||||
case rapidjson::kObjectType:
|
||||
entryStack.push(EndObjectMarker{});
|
||||
entryCountStack.push(0);
|
||||
result = visitor->StartObject();
|
||||
result = visitor.StartObject();
|
||||
for (auto it = currentValue.MemberEnd(); it != currentValue.MemberBegin(); --it)
|
||||
{
|
||||
auto entry = (it - 1);
|
||||
@@ -616,7 +606,7 @@ namespace AZ::Dom::Json
|
||||
case rapidjson::kArrayType:
|
||||
entryStack.push(EndArrayMarker{});
|
||||
entryCountStack.push(0);
|
||||
result = visitor->StartArray();
|
||||
result = visitor.StartArray();
|
||||
for (auto it = currentValue.End(); it != currentValue.Begin(); --it)
|
||||
{
|
||||
auto entry = (it - 1);
|
||||
@@ -624,22 +614,22 @@ namespace AZ::Dom::Json
|
||||
}
|
||||
break;
|
||||
case rapidjson::kStringType:
|
||||
result = visitor->String(
|
||||
result = visitor.String(
|
||||
AZStd::string_view(currentValue.GetString(), static_cast<size_t>(currentValue.GetStringLength())),
|
||||
lifetime);
|
||||
break;
|
||||
case rapidjson::kNumberType:
|
||||
if (currentValue.IsFloat() || currentValue.IsDouble())
|
||||
{
|
||||
result = visitor->Double(currentValue.GetDouble());
|
||||
result = visitor.Double(currentValue.GetDouble());
|
||||
}
|
||||
else if (currentValue.IsInt64() || currentValue.IsInt())
|
||||
{
|
||||
result = visitor->Int64(currentValue.GetInt64());
|
||||
result = visitor.Int64(currentValue.GetInt64());
|
||||
}
|
||||
else
|
||||
{
|
||||
result = visitor->Uint64(currentValue.GetUint64());
|
||||
result = visitor.Uint64(currentValue.GetUint64());
|
||||
}
|
||||
break;
|
||||
default:
|
||||
@@ -648,23 +638,23 @@ namespace AZ::Dom::Json
|
||||
}
|
||||
else if constexpr (AZStd::is_same_v<Alternative, EndArrayMarker>)
|
||||
{
|
||||
result = visitor->EndArray(entryCountStack.top());
|
||||
result = visitor.EndArray(entryCountStack.top());
|
||||
entryCountStack.pop();
|
||||
}
|
||||
else if constexpr (AZStd::is_same_v<Alternative, EndObjectMarker>)
|
||||
{
|
||||
result = visitor->EndObject(entryCountStack.top());
|
||||
result = visitor.EndObject(entryCountStack.top());
|
||||
entryCountStack.pop();
|
||||
}
|
||||
else if constexpr (AZStd::is_same_v<Alternative, AZStd::string_view>)
|
||||
{
|
||||
if (visitor->SupportsRawKeys())
|
||||
if (visitor.SupportsRawKeys())
|
||||
{
|
||||
visitor->RawKey(arg, lifetime);
|
||||
visitor.RawKey(arg, lifetime);
|
||||
}
|
||||
else
|
||||
{
|
||||
visitor->Key(AZ::Name(arg));
|
||||
visitor.Key(AZ::Name(arg));
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
#include <AzCore/IO/GenericStreams.h>
|
||||
#include <AzCore/IO/Path/Path.h>
|
||||
#include <AzCore/JSON/document.h>
|
||||
#include <AzCore/std/containers/deque.h>
|
||||
#include <AzCore/std/containers/vector.h>
|
||||
#include <AzCore/std/smart_ptr/unique_ptr.h>
|
||||
|
||||
@@ -25,36 +26,84 @@ namespace AZ::Dom::Json
|
||||
PrettyPrintedJson, //!< Formats JSON in a pretty printed form, focusing on legibility to readers.
|
||||
};
|
||||
|
||||
//! Visitor that feeds into a rapidjson::Value
|
||||
class RapidJsonValueWriter final : public Visitor
|
||||
{
|
||||
public:
|
||||
RapidJsonValueWriter(rapidjson::Value& outputValue, rapidjson::Value::AllocatorType& allocator);
|
||||
|
||||
VisitorFlags GetVisitorFlags() const override;
|
||||
Result Null() override;
|
||||
Result Bool(bool value) override;
|
||||
Result Int64(AZ::s64 value) override;
|
||||
Result Uint64(AZ::u64 value) override;
|
||||
Result Double(double value) override;
|
||||
|
||||
Result String(AZStd::string_view value, Lifetime lifetime) override;
|
||||
Result StartObject() override;
|
||||
Result EndObject(AZ::u64 attributeCount) override;
|
||||
Result Key(AZ::Name key) override;
|
||||
Result RawKey(AZStd::string_view key, Lifetime lifetime) override;
|
||||
Result StartArray() override;
|
||||
Result EndArray(AZ::u64 elementCount) override;
|
||||
|
||||
private:
|
||||
Result FinishWrite();
|
||||
rapidjson::Value& CurrentValue();
|
||||
|
||||
struct ValueInfo
|
||||
{
|
||||
ValueInfo(bool isObject, rapidjson::Value& container);
|
||||
|
||||
rapidjson::Value m_key;
|
||||
rapidjson::Value m_value;
|
||||
rapidjson::Value& m_container;
|
||||
AZ::u64 m_entryCount = 0;
|
||||
bool m_isObject;
|
||||
};
|
||||
|
||||
rapidjson::Value& m_result;
|
||||
rapidjson::Value::AllocatorType& m_allocator;
|
||||
AZStd::deque<ValueInfo> m_entryStack;
|
||||
};
|
||||
|
||||
//! Creates a Visitor that will write serialized JSON to the specified stream.
|
||||
//! \param stream The stream the visitor will write to.
|
||||
//! \param format The format to write in.
|
||||
//! \return A Visitor that will write to stream when visited.
|
||||
AZStd::unique_ptr<Visitor> GetJsonStreamWriter(
|
||||
AZ::IO::GenericStream* stream, OutputFormatting format = OutputFormatting::PrettyPrintedJson);
|
||||
AZ::IO::GenericStream& stream, OutputFormatting format = OutputFormatting::PrettyPrintedJson);
|
||||
//! Reads serialized JSON from a string and applies it to a visitor.
|
||||
//! \param buffer The UTF-8 serialized JSON to read.
|
||||
//! \param lifetime Specifies the lifetime of the specified buffer. If the string specified by buffer might be deallocated,
|
||||
//! ensure specify Lifetime::Temporary is specified.
|
||||
//! \param visitor The visitor to visit with the JSON buffer's contents.
|
||||
//! \return The aggregate result specifying whether the visitor operations were successful.
|
||||
Visitor::Result VisitSerializedJson(AZStd::string_view buffer, Lifetime lifetime, Visitor* visitor);
|
||||
Visitor::Result VisitSerializedJson(AZStd::string_view buffer, Lifetime lifetime, Visitor& visitor);
|
||||
//! Reads serialized JSON from a string in-place and applies it to a visitor.
|
||||
//! \param buffer The UTF-8 serialized JSON to read. This buffer will be modified as part of the deserialization process to
|
||||
//! apply null terminators.
|
||||
//! \param visitor The visitor to visit with the JSON buffer's contents. The strings provided to the visitor will only
|
||||
//! be valid for the lifetime of buffer.
|
||||
//! \return The aggregate result specifying whether the visitor operations were successful.
|
||||
Visitor::Result VisitSerializedJsonInPlace(AZStd::string& buffer, Visitor* visitor);
|
||||
Visitor::Result VisitSerializedJsonInPlace(AZStd::string& buffer, Visitor& visitor);
|
||||
|
||||
//! Takes a visitor specified by a callback and produces a rapidjson::Document.
|
||||
//! \param writeCallback A callback specifying a visitor to accept to build the resulting document.
|
||||
//! \return An outcome with either the rapidjson::Document or an error message.
|
||||
AZ::Outcome<rapidjson::Document, AZStd::string> WriteToRapidJsonDocument(Backend::WriteCallback writeCallback);
|
||||
//! Takes a visitor specified by a callback and reads them into a rapidjson::Value.
|
||||
//! \param value The value to read into, its contents will be overridden.
|
||||
//! \param allocator The allocator to use when performing rapidjson allocations (generally provded by the rapidjson::Document).
|
||||
//! \param writeCallback A callback specifying a visitor to accept to build the resulting document.
|
||||
//! \return An outcome with either the rapidjson::Document or an error message.
|
||||
Visitor::Result WriteToRapidJsonValue(
|
||||
rapidjson::Value& value, rapidjson::Value::AllocatorType& allocator, Backend::WriteCallback writeCallback);
|
||||
//! Accepts a visitor with the contents of a rapidjson::Value.
|
||||
//! \param value The rapidjson::Value to apply to visitor.
|
||||
//! \param visitor The visitor to receive the contents of value.
|
||||
//! \param lifetime The lifetime to specify for visiting strings. If the rapidjson::Value might be destroyed or changed
|
||||
//! before the visitor is finished using these values, Lifetime::Temporary should be specified.
|
||||
//! \return The aggregate result specifying whether the visitor operations were successful.
|
||||
Visitor::Result VisitRapidJsonValue(const rapidjson::Value& value, Visitor* visitor, Lifetime lifetime);
|
||||
Visitor::Result VisitRapidJsonValue(const rapidjson::Value& value, Visitor& visitor, Lifetime lifetime);
|
||||
} // namespace AZ::Dom::Json
|
||||
|
||||
@@ -10,11 +10,11 @@
|
||||
|
||||
#include <AzCore/Utils/Utils.h>
|
||||
#include <AzCore/IO/ByteContainerStream.h>
|
||||
#include "DomBackend.h"
|
||||
#include <AzCore/DOM/DomBackend.h>
|
||||
|
||||
namespace AZ::Dom
|
||||
{
|
||||
Visitor::Result Backend::ReadFromStream(AZ::IO::GenericStream* stream, Visitor* visitor, size_t maxSize)
|
||||
Visitor::Result Backend::ReadFromStream(AZ::IO::GenericStream* stream, Visitor& visitor, size_t maxSize)
|
||||
{
|
||||
size_t length = stream->GetLength();
|
||||
if (length > maxSize)
|
||||
@@ -27,21 +27,21 @@ namespace AZ::Dom
|
||||
return ReadFromString(buffer, Lifetime::Temporary, visitor);
|
||||
}
|
||||
|
||||
Visitor::Result Backend::ReadFromStringInPlace(AZStd::string& buffer, Visitor* visitor)
|
||||
Visitor::Result Backend::ReadFromStringInPlace(AZStd::string& buffer, Visitor& visitor)
|
||||
{
|
||||
return ReadFromString(buffer, Lifetime::Persistent, visitor);
|
||||
}
|
||||
|
||||
Visitor::Result Backend::WriteToStream(AZ::IO::GenericStream* stream, WriteCallback callback)
|
||||
Visitor::Result Backend::WriteToStream(AZ::IO::GenericStream& stream, WriteCallback callback)
|
||||
{
|
||||
AZStd::unique_ptr<Visitor> writer = CreateStreamWriter(stream);
|
||||
return callback(writer.get());
|
||||
return callback(*writer.get());
|
||||
}
|
||||
|
||||
Visitor::Result Backend::WriteToString(AZStd::string& buffer, WriteCallback callback)
|
||||
{
|
||||
AZ::IO::ByteContainerStream<AZStd::string> stream{&buffer};
|
||||
AZStd::unique_ptr<Visitor> writer = CreateStreamWriter(&stream);
|
||||
return callback(writer.get());
|
||||
AZStd::unique_ptr<Visitor> writer = CreateStreamWriter(stream);
|
||||
return callback(*writer.get());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,24 +25,24 @@ namespace AZ::Dom
|
||||
//! Attempt to read this format from the given stream into the target Visitor.
|
||||
//! The base implementation reads the stream into memory and calls ReadFromString.
|
||||
virtual Visitor::Result ReadFromStream(
|
||||
AZ::IO::GenericStream* stream, Visitor* visitor, size_t maxSize = AZStd::numeric_limits<size_t>::max());
|
||||
AZ::IO::GenericStream* stream, Visitor& visitor, size_t maxSize = AZStd::numeric_limits<size_t>::max());
|
||||
//! Attempt to read this format from a mutable string into the target Visitor. This enables some backends to
|
||||
//! parse without making additional string allocations.
|
||||
//! This string may be modified and read in place without being copied, so when calling this please ensure that:
|
||||
//! - The string won't be deallocated until the visitor no longer needs the values and
|
||||
//! - The string is safe to modify in place.
|
||||
//! The base implementation simply calls ReadFromString.
|
||||
virtual Visitor::Result ReadFromStringInPlace(AZStd::string& buffer, Visitor* visitor);
|
||||
virtual Visitor::Result ReadFromStringInPlace(AZStd::string& buffer, Visitor& visitor);
|
||||
//! Attempt to read this format from an immutable buffer in memory into the target Visitor.
|
||||
virtual Visitor::Result ReadFromString(AZStd::string_view buffer, Lifetime lifetime, Visitor* visitor) = 0;
|
||||
virtual Visitor::Result ReadFromString(AZStd::string_view buffer, Lifetime lifetime, Visitor& visitor) = 0;
|
||||
|
||||
//! Acquire a visitor interface for writing to the target output file.
|
||||
virtual AZStd::unique_ptr<Visitor> CreateStreamWriter(AZ::IO::GenericStream* stream) = 0;
|
||||
virtual AZStd::unique_ptr<Visitor> CreateStreamWriter(AZ::IO::GenericStream& stream) = 0;
|
||||
//! A callback that accepts a Visitor, making DOM calls to inform the serializer, and returns an
|
||||
//! aggregate error code to indicate whether or not the operation succeeded.
|
||||
using WriteCallback = AZStd::function<Visitor::Result(Visitor*)>;
|
||||
using WriteCallback = AZStd::function<Visitor::Result(Visitor&)>;
|
||||
//! Attempt to write a value to a stream using a write callback.
|
||||
Visitor::Result WriteToStream(AZ::IO::GenericStream* stream, WriteCallback callback);
|
||||
Visitor::Result WriteToStream(AZ::IO::GenericStream& stream, WriteCallback callback);
|
||||
//! Attempt to write a value to a string using a write callback.
|
||||
Visitor::Result WriteToString(AZStd::string& buffer, WriteCallback callback);
|
||||
};
|
||||
|
||||
@@ -130,7 +130,7 @@ namespace Benchmark
|
||||
state.ResumeTiming();
|
||||
|
||||
auto result = AZ::Dom::Json::WriteToRapidJsonDocument(
|
||||
[&](AZ::Dom::Visitor* visitor)
|
||||
[&](AZ::Dom::Visitor& visitor)
|
||||
{
|
||||
return backend.ReadFromStringInPlace(payloadCopy, visitor);
|
||||
});
|
||||
@@ -150,7 +150,7 @@ namespace Benchmark
|
||||
for (auto _ : state)
|
||||
{
|
||||
auto result = AZ::Dom::Json::WriteToRapidJsonDocument(
|
||||
[&](AZ::Dom::Visitor* visitor)
|
||||
[&](AZ::Dom::Visitor& visitor)
|
||||
{
|
||||
return backend.ReadFromString(serializedPayload, AZ::Dom::Lifetime::Temporary, visitor);
|
||||
});
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include <AzCore/DOM/Backends/JSON/JsonBackend.h>
|
||||
#include <AzCore/DOM/Backends/JSON/JsonSerializationUtils.h>
|
||||
#include <AzCore/Name/NameDictionary.h>
|
||||
#include <AzCore/Serialization/Json/JsonSerialization.h>
|
||||
#include <AzCore/Serialization/Json/JsonUtils.h>
|
||||
#include <AzCore/UnitTest/TestTypes.h>
|
||||
|
||||
@@ -31,78 +32,6 @@ namespace AZ::Dom::Tests
|
||||
UnitTest::AllocatorsFixture::TearDown();
|
||||
}
|
||||
|
||||
static bool DeepCompare(const rapidjson::Value& lhs, const rapidjson::Value& rhs)
|
||||
{
|
||||
if (lhs.GetType() != rhs.GetType())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (lhs.GetType())
|
||||
{
|
||||
case rapidjson::kNullType:
|
||||
return true;
|
||||
case rapidjson::kFalseType:
|
||||
return true;
|
||||
case rapidjson::kTrueType:
|
||||
return true;
|
||||
case rapidjson::kObjectType:
|
||||
{
|
||||
if (lhs.MemberCount() != rhs.MemberCount())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
auto lhsIt = lhs.MemberBegin();
|
||||
auto rhsIt = rhs.MemberBegin();
|
||||
while (lhsIt != lhs.MemberEnd())
|
||||
{
|
||||
if (lhsIt->name != rhsIt->name)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!DeepCompare(lhsIt->value, rhsIt->value))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
++lhsIt;
|
||||
++rhsIt;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
case rapidjson::kArrayType:
|
||||
{
|
||||
if (lhs.Size() != rhs.Size())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
auto lhsIt = lhs.Begin();
|
||||
auto rhsIt = rhs.Begin();
|
||||
while (lhsIt != lhs.End())
|
||||
{
|
||||
if (!DeepCompare(*lhsIt, *rhsIt))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
++lhsIt;
|
||||
++rhsIt;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
case rapidjson::kStringType:
|
||||
return lhs == rhs;
|
||||
case rapidjson::kNumberType:
|
||||
return lhs == rhs;
|
||||
}
|
||||
|
||||
AZ_Assert(false, "Unexpected JSON value type");
|
||||
return false;
|
||||
}
|
||||
|
||||
rapidjson::Value CreateString(const AZStd::string& text)
|
||||
{
|
||||
rapidjson::Value key;
|
||||
@@ -110,7 +39,7 @@ namespace AZ::Dom::Tests
|
||||
return key;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
template<class T>
|
||||
void AddValue(const AZStd::string& key, T value)
|
||||
{
|
||||
m_document->AddMember(CreateString(key), rapidjson::Value(value), m_document->GetAllocator());
|
||||
@@ -125,7 +54,7 @@ namespace AZ::Dom::Tests
|
||||
AZStd::string canonicalSerializedDocument;
|
||||
AZ::JsonSerializationUtils::WriteJsonString(*m_document, canonicalSerializedDocument);
|
||||
|
||||
auto visitDocumentFn = [this](AZ::Dom::Visitor* visitor)
|
||||
auto visitDocumentFn = [this](AZ::Dom::Visitor& visitor)
|
||||
{
|
||||
return Json::VisitRapidJsonValue(*m_document, visitor, Lifetime::Persistent);
|
||||
};
|
||||
@@ -134,7 +63,7 @@ namespace AZ::Dom::Tests
|
||||
{
|
||||
auto result = Json::WriteToRapidJsonDocument(visitDocumentFn);
|
||||
EXPECT_TRUE(result.IsSuccess());
|
||||
EXPECT_TRUE(DeepCompare(*m_document, result.GetValue()));
|
||||
EXPECT_EQ(AZ::JsonSerialization::Compare(*m_document, result.GetValue()), AZ::JsonSerializerCompareResult::Equal);
|
||||
}
|
||||
|
||||
// Document -> string
|
||||
@@ -149,13 +78,13 @@ namespace AZ::Dom::Tests
|
||||
// string -> Document
|
||||
{
|
||||
auto result = Json::WriteToRapidJsonDocument(
|
||||
[&canonicalSerializedDocument](AZ::Dom::Visitor* visitor)
|
||||
[&canonicalSerializedDocument](AZ::Dom::Visitor& visitor)
|
||||
{
|
||||
JsonBackend backend;
|
||||
return backend.ReadFromString(canonicalSerializedDocument, AZ::Dom::Lifetime::Temporary, visitor);
|
||||
});
|
||||
EXPECT_TRUE(result.IsSuccess());
|
||||
EXPECT_TRUE(DeepCompare(*m_document, result.GetValue()));
|
||||
EXPECT_EQ(AZ::JsonSerialization::Compare(*m_document, result.GetValue()), JsonSerializerCompareResult::Equal);
|
||||
}
|
||||
|
||||
// string -> string
|
||||
@@ -164,7 +93,7 @@ namespace AZ::Dom::Tests
|
||||
JsonBackend backend;
|
||||
auto result = backend.WriteToString(
|
||||
serializedDocument,
|
||||
[&backend, &canonicalSerializedDocument](AZ::Dom::Visitor* visitor)
|
||||
[&backend, &canonicalSerializedDocument](AZ::Dom::Visitor& visitor)
|
||||
{
|
||||
return backend.ReadFromString(canonicalSerializedDocument, AZ::Dom::Lifetime::Temporary, visitor);
|
||||
});
|
||||
@@ -282,7 +211,8 @@ namespace AZ::Dom::Tests
|
||||
m_document->SetObject();
|
||||
m_document->AddMember(CreateString("empty_string"), CreateString(""), m_document->GetAllocator());
|
||||
m_document->AddMember(CreateString("short_string"), CreateString("test"), m_document->GetAllocator());
|
||||
m_document->AddMember(CreateString("long_string"), CreateString("abcdefghijklmnopqrstuvwxyz0123456789"), m_document->GetAllocator());
|
||||
m_document->AddMember(
|
||||
CreateString("long_string"), CreateString("abcdefghijklmnopqrstuvwxyz0123456789"), m_document->GetAllocator());
|
||||
PerformSerializationChecks();
|
||||
}
|
||||
} // namespace AZ::Dom::Tests
|
||||
|
||||
Reference in New Issue
Block a user