-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Implement deserialization of JsonSerializer
Linked: #164
- Loading branch information
1 parent
4a11ad9
commit be7e08a
Showing
2 changed files
with
261 additions
and
18 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -40,12 +40,22 @@ namespace ecstasy::serialization | |
/// | ||
/// @brief Nested context operations, used to open and close nested objects and arrays in streams easily. | ||
/// | ||
/// @note Use the static constexpr instances to use them in stream operations. | ||
/// | ||
/// @author Andréas Leroux ([email protected]) | ||
/// @since 1.0.0 (2024-10-10) | ||
/// | ||
enum class NestedContextOp { NewObject, NewArray, Close }; | ||
/// @brief Alias for NestedContextOp | ||
using OP = NestedContextOp; | ||
enum class NestedContextOp { | ||
NewObject, ///< Open a new object (Creating it in save mode) | ||
NewArray, ///< Open a new array (Creating it in save mode) | ||
Close ///< Close the current object or array | ||
}; | ||
/// @copydoc NestedContextOp::NewObject | ||
static constexpr NestedContextOp NewObject = NestedContextOp::NewObject; | ||
/// @copydoc NestedContextOp::NewArray | ||
static constexpr NestedContextOp NewArray = NestedContextOp::NewArray; | ||
/// @copydoc NestedContextOp::Close | ||
static constexpr NestedContextOp Close = NestedContextOp::Close; | ||
|
||
/// | ||
/// @brief Construct a new Raw Serializer instance. | ||
|
@@ -135,6 +145,21 @@ namespace ecstasy::serialization | |
_document.Accept(writer); | ||
} | ||
|
||
/// | ||
/// @brief Reset the json read/write cursor to the begining of the document. | ||
/// | ||
/// @author Andréas Leroux ([email protected]) | ||
/// @since 1.0.0 (2024-10-11) | ||
/// | ||
void resetCursor() | ||
{ | ||
while (!_stack.empty()) | ||
_stack.pop(); | ||
while (!_arrayIterators.empty()) | ||
_arrayIterators.pop(); | ||
} | ||
|
||
/// @copydoc save | ||
// clang-format off | ||
template <typename T, | ||
typename = typename std::enable_if< | ||
|
@@ -190,6 +215,155 @@ namespace ecstasy::serialization | |
return *this; | ||
} | ||
|
||
/// @copydoc update | ||
template <typename U> | ||
requires std::is_same_v<U, NestedContextOp> | ||
JsonSerializer &updateImpl(const U &op) | ||
{ | ||
switch (op) { | ||
case NestedContextOp::NewObject: newNestedObject(false); break; | ||
case NestedContextOp::NewArray: newNestedArray(false); break; | ||
case NestedContextOp::Close: closeNested(); break; | ||
} | ||
return *this; | ||
} | ||
|
||
/// @copydoc update | ||
// clang-format off | ||
template <typename U, | ||
typename = typename std::enable_if<( | ||
std::is_same_v<U, std::string> || std::is_same_v<U, std::string_view> || // Load | ||
std::is_bounded_array_v<U> || // Bounded array | ||
util::meta::is_std_vector<U>::value) // std::vector | ||
, int>::type> | ||
// clang-format on | ||
JsonSerializer &updateImpl(U &object) | ||
{ | ||
// Handle string key for object values | ||
if constexpr (std::is_same_v<U, std::string> || std::is_same_v<U, std::string_view> | ||
|| util::meta::is_type_bounded_array_v<U, char>) { | ||
if (getWriteCursor().IsObject() && _nextKey.empty()) | ||
_nextKey = object; | ||
else { | ||
if constexpr (std::is_same_v<U, std::string>) | ||
object = load<U>(); | ||
else | ||
throw std::invalid_argument("string_view and char[] can only be used for object keys."); | ||
} | ||
} | ||
// Handle bounded arrays | ||
else if constexpr (std::is_bounded_array_v<U>) { | ||
newNestedArray(false); | ||
|
||
// Need to fetch the new cursor after the new nested array | ||
if (getWriteCursor().Size() != std::extent_v<U>) | ||
throw std::invalid_argument("Array size mismatch."); | ||
|
||
for (size_t i = 0; i < std::extent_v<U>; i++) | ||
update(object[i]); | ||
closeNested(); | ||
} | ||
// Handle std::vector | ||
else if constexpr (util::meta::is_std_vector<U>::value) { | ||
newNestedArray(false); | ||
rapidjson::SizeType size = getWriteCursor().Size(); | ||
|
||
object.clear(); | ||
object.reserve(size); | ||
for (size_t i = 0; i < size; i++) { | ||
if constexpr (is_constructible<typename U::value_type>) | ||
object.emplace_back(*this); | ||
else | ||
object.push_back(std::move(load<typename U::value_type>())); | ||
} | ||
closeNested(); | ||
} else | ||
return Parent::update(object); | ||
return *this; | ||
} | ||
|
||
/// @copydoc load | ||
// clang-format off | ||
template <typename U> | ||
requires std::is_fundamental_v<U> || // Fundamental type | ||
std::is_same_v<U, std::string> | ||
// clang-format on | ||
U loadImpl() | ||
{ | ||
const rapidjson::Value &value = readCurrentValue(true); | ||
|
||
// Read Boolean | ||
if constexpr (std::is_same_v<U, bool>) { | ||
return value.GetBool(); | ||
} | ||
// Read Integer | ||
else if constexpr (std::numeric_limits<U>::is_integer) { | ||
if constexpr (std::numeric_limits<U>::is_signed) { | ||
if constexpr (sizeof(U) < 8) | ||
return static_cast<U>(value.GetInt()); | ||
else | ||
return static_cast<U>(value.GetInt64()); | ||
} else { | ||
if constexpr (sizeof(U) < 8) | ||
return static_cast<U>(value.GetUint()); | ||
else | ||
return static_cast<U>(value.GetUint64()); | ||
} | ||
} | ||
// Read Floating point | ||
else if constexpr (std::is_floating_point_v<U>) { | ||
if constexpr (sizeof(U) == 4) | ||
return value.GetFloat(); | ||
else | ||
return value.GetDouble(); | ||
} | ||
// Read String | ||
else if constexpr (std::is_same_v<U, std::string>) { | ||
return std::string(value.GetString(), value.GetStringLength()); | ||
} | ||
} | ||
|
||
/// | ||
/// @brief Access the current value in the json document, and move the cursor to the next value. | ||
/// | ||
/// @param[in] andMoveCursor If true, move the cursor to the next value. | ||
/// | ||
/// @return rapidjson::Value& Reference to the current value. | ||
/// | ||
/// @author Andréas Leroux ([email protected]) | ||
/// @since 1.0.0 (2024-10-11) | ||
/// | ||
rapidjson::Value &readCurrentValue(bool andMoveCursor = true) | ||
{ | ||
rapidjson::Value &cursor = getWriteCursor(); | ||
|
||
if (cursor.IsObject()) { | ||
if (_nextKey.empty()) | ||
throw std::invalid_argument("No key set for object value."); | ||
rapidjson::Value &result = cursor[_nextKey.c_str()]; | ||
if (andMoveCursor) | ||
_nextKey.clear(); | ||
return result; | ||
} else { | ||
if (!cursor.IsArray()) { | ||
throw std::invalid_argument("Invalid cursor type. Expected object or array."); | ||
} | ||
// If the array is empty, push the first element (first document element) | ||
if (_arrayIterators.empty()) | ||
_arrayIterators.push(cursor.Begin()); | ||
|
||
// If we reached the end of the array, throw an exception | ||
if (_arrayIterators.top() == cursor.End()) | ||
throw std::out_of_range("End of array reached."); | ||
|
||
rapidjson::Value *value = _arrayIterators.top(); | ||
// Move the cursor to the next element | ||
if (andMoveCursor) | ||
++_arrayIterators.top(); | ||
return *value; | ||
} | ||
} | ||
|
||
/// @copydoc loadComponentHash | ||
std::size_t loadComponentHash() override final | ||
{ | ||
|
@@ -203,25 +377,32 @@ namespace ecstasy::serialization | |
/// @warning If the context is an object you must set a key before adding a value to it. | ||
/// | ||
/// @param[in] type Type of the nested object or array. | ||
/// @param[in] create If true, create the nested object or array (for save mode). | ||
/// | ||
/// @return JsonSerializer& Reference to @b this for chain calls. | ||
/// | ||
/// @author Andréas Leroux ([email protected]) | ||
/// @since 1.0.0 (2024-10-10) | ||
/// | ||
JsonSerializer &newNested(rapidjson::Type type) | ||
JsonSerializer &newNested(rapidjson::Type type, bool create = true) | ||
{ | ||
if (type != rapidjson::Type::kObjectType && type != rapidjson::Type::kArrayType) | ||
throw std::invalid_argument("Invalid type for nested object."); | ||
|
||
rapidjson::Value &cursor = getWriteCursor(); | ||
std::string key = _nextKey; | ||
if (create) { | ||
rapidjson::Value &cursor = getWriteCursor(); | ||
std::string key = _nextKey; | ||
|
||
addValue(rapidjson::Value(type)); | ||
if (cursor.IsArray()) | ||
_stack.push(cursor[cursor.Size() - 1]); | ||
else | ||
_stack.push(cursor[key.c_str()]); | ||
addValue(rapidjson::Value(type)); | ||
if (cursor.IsArray()) | ||
_stack.push(cursor[cursor.Size() - 1]); | ||
else | ||
_stack.push(cursor[key.c_str()]); | ||
} else { | ||
_stack.push(readCurrentValue()); | ||
if (getWriteCursor().IsArray()) | ||
_arrayIterators.push(getWriteCursor().Begin()); | ||
} | ||
return *this; | ||
} | ||
|
||
|
@@ -233,9 +414,9 @@ namespace ecstasy::serialization | |
/// @author Andréas Leroux ([email protected]) | ||
/// @since 1.0.0 (2024-10-10) | ||
/// | ||
JsonSerializer &newNestedObject() | ||
JsonSerializer &newNestedObject(bool create = true) | ||
{ | ||
return newNested(rapidjson::Type::kObjectType); | ||
return newNested(rapidjson::Type::kObjectType, create); | ||
} | ||
|
||
/// | ||
|
@@ -246,9 +427,9 @@ namespace ecstasy::serialization | |
/// @author Andréas Leroux ([email protected]) | ||
/// @since 1.0.0 (2024-10-10) | ||
/// | ||
JsonSerializer &newNestedArray() | ||
JsonSerializer &newNestedArray(bool create = true) | ||
{ | ||
return newNested(rapidjson::Type::kArrayType); | ||
return newNested(rapidjson::Type::kArrayType, create); | ||
} | ||
|
||
/// | ||
|
@@ -262,6 +443,8 @@ namespace ecstasy::serialization | |
/// | ||
JsonSerializer &closeNested() | ||
{ | ||
if (_stack.top().get().IsArray()) | ||
_arrayIterators.pop(); | ||
_stack.pop(); | ||
return *this; | ||
} | ||
|
@@ -318,6 +501,7 @@ namespace ecstasy::serialization | |
private: | ||
rapidjson::Document _document; | ||
std::stack<std::reference_wrapper<rapidjson::Value>> _stack; | ||
std::stack<rapidjson::Value::ValueIterator> _arrayIterators; | ||
std::string _nextKey; | ||
}; | ||
} // namespace ecstasy::serialization | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters