From 6a6ffb6779a55b4ed43a11eaa8254c4c57c8856e Mon Sep 17 00:00:00 2001 From: SweetId <2630750+SweetId@users.noreply.github.com> Date: Sun, 10 Mar 2024 17:04:15 -0400 Subject: [PATCH] [Serialization] Add Json serializer --- include/Nazara/Core/JsonSerialization.hpp | 53 +++++++++ src/Nazara/Core/JsonSerialization.cpp | 132 ++++++++++++++++++++++ xmake.lua | 5 +- 3 files changed, 188 insertions(+), 2 deletions(-) create mode 100644 include/Nazara/Core/JsonSerialization.hpp create mode 100644 src/Nazara/Core/JsonSerialization.cpp diff --git a/include/Nazara/Core/JsonSerialization.hpp b/include/Nazara/Core/JsonSerialization.hpp new file mode 100644 index 000000000..dac51df5d --- /dev/null +++ b/include/Nazara/Core/JsonSerialization.hpp @@ -0,0 +1,53 @@ + +#pragma once + +#include +#include + +namespace Nz +{ + class NAZARA_CORE_API JsonSerializationContext + : public SerializationContext + { + public: + JsonSerializationContext(); + ~JsonSerializationContext(); + + bool Load(const std::filesystem::path& path); + + EnumSerializationMode GetEnumSerializationMode() const override { return EnumSerializationMode::String; } + bool PushObject(std::string_view name) override; + bool PopObject() override; + bool PushArray(std::string_view name) override; + bool PopArray() override; + + bool Write(std::string_view name, bool value) override; + bool Write(std::string_view name, Nz::Int8 value) override; + bool Write(std::string_view name, Nz::Int16 value) override; + bool Write(std::string_view name, Nz::Int32 value) override; + bool Write(std::string_view name, Nz::Int64 value) override; + bool Write(std::string_view name, Nz::UInt8 value) override; + bool Write(std::string_view name, Nz::UInt16 value) override; + bool Write(std::string_view name, Nz::UInt32 value) override; + bool Write(std::string_view name, Nz::UInt64 value) override; + bool Write(std::string_view name, float value) override; + bool Write(std::string_view name, double value) override; + bool Write(std::string_view name, const std::string& value) override; + + bool Read(std::string_view name, bool* value) override; + bool Read(std::string_view name, Nz::Int8* value) override; + bool Read(std::string_view name, Nz::Int16* value) override; + bool Read(std::string_view name, Nz::Int32* value) override; + bool Read(std::string_view name, Nz::Int64* value) override; + bool Read(std::string_view name, Nz::UInt8* value) override; + bool Read(std::string_view name, Nz::UInt16* value) override; + bool Read(std::string_view name, Nz::UInt32* value) override; + bool Read(std::string_view name, Nz::UInt64* value) override; + bool Read(std::string_view name, float* value) override; + bool Read(std::string_view name, double* value) override; + bool Read(std::string_view name, std::string* value) override; + + private: + std::unique_ptr m_impl; + }; +} diff --git a/src/Nazara/Core/JsonSerialization.cpp b/src/Nazara/Core/JsonSerialization.cpp new file mode 100644 index 000000000..84eb7dd96 --- /dev/null +++ b/src/Nazara/Core/JsonSerialization.cpp @@ -0,0 +1,132 @@ +#include +#include +#include + +#include + +#include + +namespace Nz +{ + struct JsonImpl + { + + nlohmann::json& Top() { return *stack.top(); } + + bool Pop() + { + if (stack.empty()) + { + NazaraAssert(stack.empty(), "Stack is empty"); + return false; + } + stack.pop(); + return true; + } + + nlohmann::json json; + std::stack stack; + }; + + JsonSerializationContext::JsonSerializationContext() + : m_impl(std::make_unique()) + {} + + JsonSerializationContext::~JsonSerializationContext() {} + + bool JsonSerializationContext::Load(const std::filesystem::path& path) + { + std::ifstream file(path); + if (!file || !file.is_open()) + return false; + + m_impl->json = nlohmann::json::parse(file); + m_impl->stack.push(&m_impl->json); + return true; + } + + bool JsonSerializationContext::PushObject(std::string_view name) + { + if (name.empty()) // special case if name is empty, don't push + return true; + + if (!m_impl->Top().contains(name)) + return false; + + if (!m_impl->Top()[name].is_object()) + { + NazaraAssert(m_impl->Top()[name].is_object(), "Value is not an object"); + return false; + } + m_impl->stack.push(&m_impl->Top()[name]); + return true; + } + + bool JsonSerializationContext::PopObject() + { + return m_impl->Pop(); + } + + bool JsonSerializationContext::PushArray(std::string_view name) + { + if (!m_impl->Top()[name].is_array()) + { + NazaraAssert(m_impl->Top()[name].is_array(), "Value is not an array"); + return false; + } + m_impl->stack.push(&m_impl->Top()[name]); + return true; + } + + bool JsonSerializationContext::PopArray() + { + return m_impl->Pop(); + } + +#define DEF_SERIALIZE(Type, cond) \ + bool JsonSerializationContext::Write(std::string_view name, Type value) \ + { \ + m_impl->Top()[name] = value; \ + return true; \ + } \ + bool JsonSerializationContext::Read(std::string_view name, Type* value) \ + { \ + if (!m_impl->Top()[name].cond()) \ + { \ + /*NazaraAssert(m_impl->Top()[name].cond(), "Value failed check: " #cond "()");*/\ + return false; \ + } \ + *value = m_impl->Top()[name].get(); \ + return true; \ + } \ + + DEF_SERIALIZE(bool, is_boolean); + DEF_SERIALIZE(Nz::Int8, is_number); + DEF_SERIALIZE(Nz::Int16, is_number); + DEF_SERIALIZE(Nz::Int32, is_number); + DEF_SERIALIZE(Nz::Int64, is_number); + DEF_SERIALIZE(Nz::UInt8, is_number); + DEF_SERIALIZE(Nz::UInt16, is_number); + DEF_SERIALIZE(Nz::UInt32, is_number); + DEF_SERIALIZE(Nz::UInt64, is_number); + DEF_SERIALIZE(float, is_number); + DEF_SERIALIZE(double, is_number); +#undef DEF_SERIALIZE + + bool JsonSerializationContext::Write(std::string_view name, const std::string& value) + { + m_impl->Top()[name] = value; + return true; + } + + bool JsonSerializationContext::Read(std::string_view name, std::string* value) + { + if (!m_impl->Top()[name].is_string()) + { + NazaraAssert(m_impl->Top()[name].is_string(), "Value failed check: is_string()"); + return false; + } + *value = m_impl->Top()[name].get(); + return true; + } +} diff --git a/xmake.lua b/xmake.lua index d535ed692..d0c45222f 100644 --- a/xmake.lua +++ b/xmake.lua @@ -107,7 +107,7 @@ local modules = { remove_files("src/Nazara/Core/Posix/TimeImpl.cpp") end end, - Packages = { "concurrentqueue", "entt", "frozen", "ordered_map", "stb", "utfcpp" }, + Packages = { "concurrentqueue", "entt", "frozen", "ordered_map", "stb", "utfcpp", "nlohmann_json" }, PublicPackages = { "nazarautils" } }, Graphics = { @@ -284,7 +284,8 @@ add_requires( "ordered_map", "nazarautils >=2024.01.25", "stb", - "utfcpp" + "utfcpp", + "nlohmann_json" ) -- Don't link with system-installed libs on CI