1
0
mirror of https://github.com/OpenRCT2/OpenRCT2 synced 2026-01-24 15:24:30 +01:00
Files
OpenRCT2/src/openrct2/core/Json.hpp
2021-02-21 03:14:18 +00:00

199 lines
7.4 KiB
C++

/*****************************************************************************
* Copyright (c) 2014-2020 OpenRCT2 developers
*
* For a complete list of all authors, please refer to contributors.md
* Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2
*
* OpenRCT2 is licensed under the GNU General Public License version 3.
*****************************************************************************/
#pragma once
#include "../common.h"
#include "FileSystem.hpp"
#include <nlohmann/json.hpp>
#include <string>
#include <string_view>
#if NLOHMANN_JSON_VERSION_MAJOR < 3 || (NLOHMANN_JSON_VERSION_MAJOR == 3 && NLOHMANN_JSON_VERSION_MINOR < 6)
# error "Unsupported version of nlohmann json library, must be >= 3.6"
#endif // NLOHMANN_JSON_VERSION_MAJOR < 3 || (NLOHMANN_JSON_VERSION_MAJOR == 3 && NLOHMANN_JSON_VERSION_MINOR < 6)
using json_t = nlohmann::json;
namespace Json
{
// Don't try to load JSON files that exceed 64 MiB
constexpr uint64_t MAX_JSON_SIZE = 64 * 1024 * 1024;
/**
* Read JSON file and parse contents
* @param path Path to the source file
* @param maxSize Max file size in bytes allowed
* @return A JSON representation of the file
* @note This function will throw an exception if the JSON file cannot be parsed
*/
json_t ReadFromFile(const utf8* path, size_t maxSize = MAX_JSON_SIZE);
json_t ReadFromFile(const fs::path& path, size_t maxSize = MAX_JSON_SIZE);
/**
* Read JSON file and parse the contents
* @param path Path to the destination file
* @param jsonData A JSON object
* @param indentSize The number of spaces in an indent, or removes whitespace on -1
*/
void WriteToFile(const utf8* path, const json_t& jsonData, int indentSize = 4);
void WriteToFile(const fs::path& path, const json_t& jsonData, int indentSize = 4);
/**
* Parse JSON from a string
* @param raw JSON string
* @return A JSON representation of the string
* @note This function will throw an exception if the JSON string cannot be parsed
*/
json_t FromString(std::string_view raw);
/**
* Parse JSON from a vector of characters
* @param vec Vector of characters containing JSON
* @return A JSON representation of the vector
* @note This function will throw an exception if the JSON vector cannot be parsed
*/
json_t FromVector(const std::vector<uint8_t>& vec);
/**
* Explicit type conversion between a JSON object and a compatible number value
* @param T Destination numeric type
* @param jsonObj JSON object holding the value
* @param defaultValue Default value to return if the JSON object is not a number type
* @return Copy of the JSON value converted to the given type
*/
template<typename T> T GetNumber(const json_t& jsonObj, T defaultValue = 0)
{
static_assert(std::is_arithmetic<T>::value, "GetNumber template parameter must be arithmetic");
return jsonObj.is_number() ? jsonObj.get<T>() : defaultValue;
}
/**
* Explicit type conversion between a JSON object and a compatible enum value
* @param T Destination enum
* @param jsonObj JSON object holding the value
* @param defaultValue Default value to return if the JSON object is not an enum type
* @return Copy of the JSON value converted to the given enum type
*/
template<typename T> T GetEnum(const json_t& jsonObj, T defaultValue)
{
static_assert(std::is_enum<T>::value, "GetEnum template parameter must be an enum");
return jsonObj.is_number_integer() ? jsonObj.get<T>() : defaultValue;
}
/**
* Explicit type conversion between a JSON object and an std::string
* @param jsonObj JSON object holding the value
* @param defaultValue Default value to return if the JSON object is not a string
* @return Copy of the JSON value converted to std::string
*/
std::string GetString(const json_t& jsonObj, const std::string& defaultValue = std::string());
/**
* Explicit type conversion between a JSON object and a boolean
* @param jsonObj JSON object holding the value
* @param defaultValue Default value to return if the JSON object is not a boolean
* @return Copy of the JSON value converted to bool
*/
bool GetBoolean(const json_t& jsonObj, bool defaultValue = false);
/**
* Ensures a given JSON object is an object type
* @param jsonObj JSON object
* @return The JSON object if it is an object type, or an empty object otherwise
*/
json_t AsObject(const json_t& jsonObj);
/**
* Ensures a given JSON object is an array type
* @param jsonObj JSON object
* @return The JSON object if it is an array type, or an array containing the JSON object otherwise
*/
json_t AsArray(const json_t& jsonObj);
/**
* Helper function to convert a json object and an initializer list to binary flags
* @param T Type to return
* @param jsonObj JSON object containing boolean values
* @param list List of pairs of keys and bits to enable if that key in the object is true
* @return Value with relevant bits flipped
*/
template<typename T> T GetFlags(const json_t& jsonObj, std::initializer_list<std::pair<std::string, T>> list)
{
static_assert(std::is_convertible<T, int>::value, "GetFlags template parameter must be integral or a weak enum");
T flags{};
for (const auto& item : list)
{
if (jsonObj.contains(item.first) && Json::GetBoolean(jsonObj[item.first]))
{
flags = static_cast<T>(flags | item.second);
}
}
return flags;
}
/**
* Used by the GetFlags function to allow for inverted values
*/
enum class FlagType : uint8_t
{
// Flag is turned on if the key is true
Normal,
// Flag is turned on if the key is false
Inverted
};
/**
* Helper function to convert a json object and an initializer list to binary flags
* @param T Type to return
* @param jsonObj JSON object containing boolean values
* @param list List of tuples of keys, bits to change and flag type
* @return Value with relevant bits flipped
* @note FLAG_NORMAL behaves like the other GetFlags function, but FLAG_INVERTED will turn the flag on when false
*/
template<typename T> T GetFlags(const json_t& jsonObj, std::initializer_list<std::tuple<std::string, T, FlagType>> list)
{
static_assert(std::is_convertible<T, int>::value, "GetFlags template parameter must be integral or a weak enum");
T flags{};
for (const auto& item : list)
{
if (std::get<2>(item) == FlagType::Normal)
{
if (jsonObj.contains(std::get<0>(item)) && Json::GetBoolean(jsonObj[std::get<0>(item)]))
{
flags = static_cast<T>(flags | std::get<1>(item));
}
}
else
{
// if the json flag doesn't exist, assume it's false
if (!jsonObj.contains(std::get<0>(item)) || !Json::GetBoolean(jsonObj[std::get<0>(item)]))
{
flags = static_cast<T>(flags | std::get<1>(item));
}
}
}
return flags;
}
} // namespace Json
class JsonException final : public std::runtime_error
{
public:
explicit JsonException(const std::string& message)
: std::runtime_error(message)
{
}
};