ECSTASY
All in the name
Loading...
Searching...
No Matches
JsonSerializer.hpp
Go to the documentation of this file.
1
11
12#ifndef ECSTASY_SERIALIZATION_JSONSERIALIZER_HPP_
13#define ECSTASY_SERIALIZATION_JSONSERIALIZER_HPP_
14
15#include <cstdint>
16#include <rapidjson/document.h>
17#include <rapidjson/istreamwrapper.h>
18#include <rapidjson/ostreamwrapper.h>
19#include <rapidjson/pointer.h>
20#include <rapidjson/writer.h>
21#include <sstream>
22#include <stack>
23#include <vector>
24
25#include "Serializer.hpp"
28
30{
31
38 class JsonSerializer : public Serializer<JsonSerializer> {
39 public:
48 enum class NestedContextOp {
49 NewObject,
50 NewArray,
51 Close
52 };
59
67 {
69 };
70
80 {
81 importBytes(content);
82 }
83
92 explicit JsonSerializer(const std::filesystem::path &filename)
93 {
94 importFile(filename);
95 }
96
105 {
106 importStream(stream);
107 }
108
115 ~JsonSerializer() override = default;
116
118 void clear() override final
119 {
122 }
123
125 [[nodiscard]] size_t size() const override final
126 {
127 return exportBytes().size();
128 }
129
131 void importStream(std::istream &stream) override final
132 {
133 rapidjson::IStreamWrapper isw(stream);
134
135 clear();
137 }
138
140 void exportStream(std::ostream &stream) const override final
141 {
142 rapidjson::OStreamWrapper osw(stream);
144
145 _document.Accept(writer);
146 }
147
155 {
156 while (!_stack.empty())
157 _stack.pop();
158 while (!_arrayIterators.empty())
159 _arrayIterators.pop();
160 }
161
163 // clang-format off
164 template <typename T,
165 typename = typename std::enable_if<
166 (std::is_same_v<T, std::string> || std::is_same_v<T, std::string_view>) || // String
167 std::is_bounded_array_v<T> || // Bounded array
169 std::is_fundamental_v<T> || // Fundamental type (int, float, etc.)
170 std::is_same_v<T, std::type_info> || // std::type_info
171 std::is_same_v<T, NestedContextOp> // Nested context operation
172 , int>::type >
173 // clang-format on
174 JsonSerializer &saveImpl(const T &object)
175 {
176 if constexpr (std::is_same_v<T, NestedContextOp>) {
177 switch (object) {
180 case NestedContextOp::Close: closeNested(); break;
181 }
182 } else if constexpr (std::is_same_v<T, std::string> || std::is_same_v<T, std::string_view>
183 || util::meta::is_type_bounded_array_v<T, char>) {
184 rapidjson::Value value;
185
186 if constexpr (std::is_same_v<T, std::string>)
187 value.SetString(
188 object.c_str(), static_cast<rapidjson::SizeType>(object.length()), _document.GetAllocator());
189 else if constexpr (std::is_same_v<T, std::string_view>)
190 value.SetString(
191 object.data(), static_cast<rapidjson::SizeType>(object.length()), _document.GetAllocator());
192 else
193 value.SetString(object, _document.GetAllocator());
194
195 addValue(std::move(value.Move()));
196 } else if constexpr (std::is_bounded_array_v<T>) {
198
199 for (size_t i = 0; i < std::extent_v<T>; i++)
200 array.PushBack(object[i], _document.GetAllocator());
201 addValue(std::move(array.Move()));
202 } else if constexpr (util::meta::is_std_vector<T>::value) {
204
205 for (const auto &elem : object)
206 array.PushBack(elem, _document.GetAllocator());
207 addValue(std::move(array.Move()));
208 } else if constexpr (std::is_same_v<T, std::type_info>) {
209 // Will raise an exception if the serializer is not registered
211
212 if (getWriteCursor().IsObject()) {
213 save(type.getTypeName());
214 } else
215 save(type.getHash());
216 } else if constexpr (std::is_fundamental_v<T>) {
217 addValue(rapidjson::Value(object));
218 } else {
219 return Parent::save(object);
220 }
221 return *this;
222 }
223
225 template <typename U>
226 requires std::is_same_v<U, NestedContextOp>
227 JsonSerializer &updateImpl(const U &object)
228 {
229 switch (object) {
231 case NestedContextOp::NewArray: newNestedArray(false); break;
232 case NestedContextOp::Close: closeNested(); break;
233 }
234 return *this;
235 }
236
238 // clang-format off
239 template <typename U,
240 typename = typename std::enable_if<(
241 std::is_same_v<U, std::string> || std::is_same_v<U, std::string_view> || // Load
242 std::is_bounded_array_v<U> || // Bounded array
244 , int>::type>
245 // clang-format on
247 {
248 // Handle string key for object values
249 if constexpr (std::is_same_v<U, std::string> || std::is_same_v<U, std::string_view>
250 || util::meta::is_type_bounded_array_v<U, char>) {
251 if (getWriteCursor().IsObject() && _nextKey.empty())
252 _nextKey = object;
253 else {
254 if constexpr (std::is_same_v<U, std::string>)
255 object = load<U>();
256 else
257 throw std::invalid_argument("string_view and char[] can only be used for object keys.");
258 }
259 }
260 // Handle bounded arrays
261 else if constexpr (std::is_bounded_array_v<U>) {
262 newNestedArray(false);
263
264 // Need to fetch the new cursor after the new nested array
265 if (getWriteCursor().Size() != std::extent_v<U>)
266 throw std::invalid_argument("Array size mismatch.");
267
268 for (size_t i = 0; i < std::extent_v<U>; i++)
269 update(object[i]);
270 closeNested();
271 }
272 // Handle std::vector
273 else if constexpr (util::meta::is_std_vector<U>::value) {
274 newNestedArray(false);
276
277 object.clear();
278 object.reserve(size);
279 for (size_t i = 0; i < size; i++) {
280 if constexpr (is_constructible<typename U::value_type>)
281 object.emplace_back(*this);
282 else
283 object.push_back(std::move(load<typename U::value_type>()));
284 }
285 closeNested();
286 } else
287 return Parent::update(object);
288 return *this;
289 }
290
292 // clang-format off
293 template <typename U>
294 requires std::is_fundamental_v<U> || // Fundamental type
295 std::is_same_v<U, std::string>
296 // clang-format on
297 [[nodiscard]] U loadImpl()
298 {
299 const rapidjson::Value &value = readCurrentValue(true);
300
301 // Read Boolean
302 if constexpr (std::is_same_v<U, bool>) {
303 return value.GetBool();
304 }
305 // Read Integer
306 else if constexpr (std::numeric_limits<U>::is_integer) {
307 if constexpr (std::numeric_limits<U>::is_signed) {
308 if constexpr (sizeof(U) < 8)
309 return static_cast<U>(value.GetInt());
310 else
311 return static_cast<U>(value.GetInt64());
312 } else {
313 if constexpr (sizeof(U) < 8)
314 return static_cast<U>(value.GetUint());
315 else
316 return static_cast<U>(value.GetUint64());
317 }
318 }
319 // Read Floating point
320 else if constexpr (std::is_floating_point_v<U>) {
321 if constexpr (sizeof(U) == 4)
322 return value.GetFloat();
323 else
324 return value.GetDouble();
325 }
326 // Read String
327 else if constexpr (std::is_same_v<U, std::string>) {
328 return std::string(value.GetString(), value.GetStringLength());
329 }
330 }
331
342 [[nodiscard]] rapidjson::Value &readCurrentValue(bool andMoveCursor = true)
343 {
345
346 if (cursor.IsObject()) {
347 if (_nextKey.empty())
348 throw std::invalid_argument("No key set for object value.");
349 rapidjson::Value &result = cursor[_nextKey.c_str()];
350 if (andMoveCursor)
351 _nextKey.clear();
352 return result;
353 } else {
354 if (!cursor.IsArray()) {
355 throw std::invalid_argument("Invalid cursor type. Expected object or array.");
356 }
357 // If the array is empty, push the first element (first document element)
358 if (_arrayIterators.empty())
359 _arrayIterators.push(cursor.Begin());
360
361 // If we reached the end of the array, throw an exception
362 if (_arrayIterators.top() == cursor.End())
363 throw std::out_of_range("End of array reached.");
364
365 rapidjson::Value *value = _arrayIterators.top();
366 // Move the cursor to the next element
367 if (andMoveCursor)
368 ++_arrayIterators.top();
369 return *value;
370 }
371 }
372
387 JsonSerializer &newNested(rapidjson::Type type, bool create = true)
388 {
389 if (type != rapidjson::kObjectType && type != rapidjson::kArrayType)
390 throw std::invalid_argument("Invalid type for nested object.");
391
392 if (create) {
394 std::string key = _nextKey;
395
397 if (cursor.IsArray())
398 _stack.push(cursor[cursor.Size() - 1]);
399 else
400 _stack.push(cursor[key.c_str()]);
401 } else {
402 _stack.push(readCurrentValue());
403 if (getWriteCursor().IsArray())
404 _arrayIterators.push(getWriteCursor().Begin());
405 }
406 return *this;
407 }
408
417 JsonSerializer &newNestedObject(bool create = true)
418 {
419 return newNested(rapidjson::kObjectType, create);
420 }
421
430 JsonSerializer &newNestedArray(bool create = true)
431 {
432 return newNested(rapidjson::kArrayType, create);
433 }
434
445 {
446 if (_stack.top().get().IsArray() && !_arrayIterators.empty())
447 _arrayIterators.pop();
448 _stack.pop();
449 return *this;
450 }
451
460 [[nodiscard]] rapidjson::Value &getWriteCursor() noexcept
461 {
462 if (_stack.empty())
463 return _document;
464 return _stack.top();
465 }
466
481 {
483
484 if (cursor.IsObject()) {
485 if (_nextKey.empty()) {
486 if (value.IsString())
487 _nextKey = value.GetString();
488 else
489 throw std::logic_error("Json object key is missing.");
490 } else {
491 cursor.AddMember(
493 _nextKey.clear();
494 }
495 } else {
496 cursor.PushBack(value, _document.GetAllocator());
497 }
498 return *this;
499 }
500
510 {
512
513 if (!cursor.IsArray()) {
514 throw std::invalid_argument("Invalid cursor type. Expected object or array.");
515 }
516 // If the array is empty, push the first element (first document element)
517 if (_arrayIterators.empty())
518 _arrayIterators.push(cursor.Begin());
519 return _arrayIterators.top() == cursor.End();
520 }
521
531 {
533
534 if (!cursor.IsArray()) {
535 throw std::invalid_argument("Invalid cursor type. Expected object or array.");
536 }
537 return cursor.Size();
538 }
539
540 private:
551
554 {
555 if (getWriteCursor().IsObject()) {
556 if (_objectIterators.empty())
557 throw std::logic_error(
558 "No object iterator. This function should be called in an updateEntity context.");
559 if (_objectIterators.top() == getWriteCursor().MemberEnd())
560 return std::nullopt;
561 _nextKey = _objectIterators.top()->name.GetString();
562 ++_objectIterators.top();
564 } else
566 .get(load<std::size_t>())
568 }
569
571 void beforeSaveEntity([[maybe_unused]] RegistryEntity &entity) override final
572 {
574 }
575
577 void afterSaveEntity([[maybe_unused]] RegistryEntity &entity) override final
578 {
579 closeNested();
580 }
581
583 void beforeUpdateEntity([[maybe_unused]] RegistryEntity &entity) override final
584 {
585 newNestedObject(false);
586 _objectIterators.push(getWriteCursor().MemberBegin());
587 }
588
590 void afterUpdateEntity([[maybe_unused]] RegistryEntity &entity) override final
591 {
592 _objectIterators.pop();
593 closeNested();
594 }
595 };
596} // namespace ecstasy::serialization
597
598#endif /* !ECSTASY_SERIALIZATION_JSONSERIALIZER_HPP_ */
T c_str(T... args)
Entity containing a reference to the registry.
Type erased interface for cross-platform type information.
Definition AType.hpp:46
virtual size_t getHash() const noexcept=0
Get the cross-platform hash of the type name.
ecstasy::serialization::IEntityComponentSerializer & getSerializer() const noexcept
Get a reference to the entity component serializer for the given serializer type.
Definition AType.hpp:175
virtual std::string_view getTypeName() const noexcept=0
Get the name of T.
static TypeRegistry & getInstance() noexcept
Get the Instance object.
AType & get(const T &target) const
Get a reference to the AType instance matching the target.
void clear() override final
Clear the serializer content.
rapidjson::Document _document
RapidJson document.
JsonSerializer(std::istream &stream)
Construct a new Raw Serializer and import the content from a stream.
void afterSaveEntity(RegistryEntity &entity) override final
Optional method triggered at the end of saveEntity.
std::string _nextKey
Next key to use for the next object value.
static constexpr NestedContextOp Close
Close the current object or array.
std::stack< std::reference_wrapper< rapidjson::Value > > _stack
Stack of nested objects or arrays, to keep track of the current context.
JsonSerializer & newNestedArray(bool create=true)
Wrapper for JsonSerializer::newNested() with rapidjson::kArrayType as parameter.
NestedContextOp
Nested context operations, used to open and close nested objects and arrays in streams easily.
@ NewArray
Open a new array (Creating it in save mode)
@ NewObject
Open a new object (Creating it in save mode)
static constexpr NestedContextOp NewArray
Open a new array (Creating it in save mode)
JsonSerializer & closeNested()
Close the current nested object or array opened with newNestedObject or newNestedArray (or newNested)...
JsonSerializer & newNested(rapidjson::Type type, bool create=true)
Open a new nested object or array context in the current object (see getWriteCursor).
JsonSerializer & updateImpl(const U &object)
Update an existing object from the serializer.
JsonSerializer(const std::string &content)
Construct a new Raw Serializer and import the content from a string.
void resetCursor()
Reset the json read/write cursor to the begining of the document.
rapidjson::Value & getWriteCursor() noexcept
Get a reference to the current cursor (ie nested objects or arrays).
JsonSerializer & addValue(rapidjson::Value &&value)
Add a value to the current object or array.
size_t getArraySize()
Get the size of the current array.
OptionalEntityComponentSerializer loadComponentSerializer() override final
Load the next component serializer from the stream.
JsonSerializer & newNestedObject(bool create=true)
Wrapper for JsonSerializer::newNested() with rapidjson::kObjectType as parameter.
void exportStream(std::ostream &stream) const override final
Export the serializer content to a stream.
void afterUpdateEntity(RegistryEntity &entity) override final
Optional method triggered at the end of updateEntity.
JsonSerializer()
Construct a new Raw Serializer instance.
JsonSerializer & updateImpl(U &object)
Update an existing object from the serializer.
void beforeSaveEntity(RegistryEntity &entity) override final
Optional method triggered at the start of saveEntity.
JsonSerializer & saveImpl(const T &object)
Save an object to the serializer.
bool isArrayEnd()
Check if the current cursor is at the end of an array.
std::stack< rapidjson::Value::MemberIterator > _objectIterators
Stack of object iterators, to keep track of the current object element.
std::stack< rapidjson::Value::ValueIterator > _arrayIterators
Stack of array iterators, to keep track of the current array element.
rapidjson::Value & readCurrentValue(bool andMoveCursor=true)
Access the current value in the json document, and move the cursor to the next value.
void importStream(std::istream &stream) override final
Import data from a stream into the serializer.
U loadImpl()
Load an object from the serializer.
void beforeUpdateEntity(RegistryEntity &entity) override final
Optional method triggered at the start of updateEntity.
static constexpr NestedContextOp NewObject
Open a new object (Creating it in save mode)
size_t size() const override final
Get the size of the serializer content (in bytes).
~JsonSerializer() override=default
Destroy the JsonSerializer.
JsonSerializer(const std::filesystem::path &filename)
Construct a new Raw Serializer and import the content from a file.
void importFile(const std::filesystem::path &filename) override
Import data from a file into the serializer.
JsonSerializer & update(U &object)
Update an existing object from the serializer.
JsonSerializer & save(const U &object)
Save an object to the serializer.
std::string exportBytes() const override
Export the serializer content to a string.
void importBytes(const std::string &content) override
Import data from a string into the serializer.
GenericDocument & ParseStream(InputStream &is)
SizeType GetStringLength() const
GenericValue & Move() RAPIDJSON_NOEXCEPT
GenericValue & PushBack(GenericValue &value, Allocator &allocator)
GenericValue & AddMember(GenericValue &name, GenericValue &value, Allocator &allocator)
GenericValue & SetArray()
float GetFloat() const
SizeType Size() const
ValueIterator Begin()
double GetDouble() const
ValueIterator End()
GenericValue & SetString(const Ch *s, SizeType length)
bool Accept(Handler &handler) const
T clear(T... args)
T empty(T... args)
Check if a type is a std::vector.
Check if a type is a bounded array of a given element type.
Namespace regrouping the serialization ecstasy classes.
Definition AType.hpp:26
unsigned SizeType
T size(T... args)
Check if a type is a std::vector.
Serialize and deserialize objects.