first commit

This commit is contained in:
Vyn 2024-11-11 16:36:37 +01:00
commit eb5191a722
15 changed files with 1075 additions and 0 deletions

4
.gitignore vendored Normal file
View file

@ -0,0 +1,4 @@
build
.cache
build-profiler.sh
build-run.sh

36
CMakeLists.txt Normal file
View file

@ -0,0 +1,36 @@
cmake_minimum_required(VERSION 3.21)
project(rei-json LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 23)
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
set(SLINT_FEATURE_RENDERER_SKIA ON)
set(SLINT_FEATURE_RENDERER_SOFTWARE ON)
add_library(rei-json
src/parse.cpp
src/toString.cpp
src/Object.cpp
src/Array.cpp
src/Field.cpp
)
target_include_directories(rei-json PRIVATE "external")
target_include_directories(rei-json PRIVATE "include")
Include(FetchContent)
FetchContent_Declare(
Catch2
GIT_REPOSITORY https://github.com/catchorg/Catch2.git
GIT_TAG v3.4.0 # or a later release
)
FetchContent_MakeAvailable(Catch2)
add_executable(tests
tests/usage.cpp
tests/parsing.cpp
tests/stringify.cpp
)
target_include_directories(tests PRIVATE "include")
target_link_libraries(tests PRIVATE rei-json)
target_link_libraries(tests PRIVATE Catch2::Catch2WithMain)

44
README.md Normal file
View file

@ -0,0 +1,44 @@
# rei-cpp
> [!warning]
> This is a work in progress, not stable and the API might change.
A C++ JSON library.
## Example
```cpp
#include "rei-json/json.h"
#include <print>
int main() {
auto objectJson = rei::json::JsonObject{};
objectJson
.set("keyPositiveNumber", 12)
.set("keyNegativeNumber", -13)
.set("keyBooleanTrue", true)
.set("keyBooleanFalse", false)
.set("keyString", "YEP")
.set("keyEmptyString", "")
.setNull("keyNull");
rei::json::JsonObject obj{};
obj.set("keyNumberOnObject", 42);
rei::json::JsonArray array{};
array.push(42);
array.push("elemString");
array.push("");
array.push(true);
array.push(false);
array.pushNull();
objectJson.addObject("keyObject", std::move(obj));
objectJson.addArray("keyArray", std::move(array));
auto newJsonString = rei::json::toString(objectJson);
std::println("{}\n", newJsonString);
return 0;
}
```

42
include/rei-json/Array.h Normal file
View file

@ -0,0 +1,42 @@
#pragma once
#include <cstddef>
#include <functional>
#include <string>
#include <vector>
namespace rei::json {
class JsonObject;
class FieldValue;
class JsonArray {
public:
JsonArray();
JsonArray& push(int value);
JsonArray& push(bool value);
JsonArray& push(const std::string& value);
JsonArray& push(const std::string&& value);
JsonArray& push(const char * value);
JsonArray& pushObject(const JsonObject& object);
JsonArray& pushObject(const JsonObject&& object);
JsonArray& pushArray(const JsonArray& array);
JsonArray& pushArray(const JsonArray&& array);
JsonArray& pushNull();
int getNumber(int index);
bool getBool(int index);
std::string getString(int index);
JsonObject& getObject(int index);
JsonArray& getArray(int index);
bool hasNull(int index);
void forEach(std::function<void(FieldValue&, int)>);
size_t count() const;
private:
std::vector<FieldValue> elements;
};
}

70
include/rei-json/Field.h Normal file
View file

@ -0,0 +1,70 @@
#pragma once
#include "Object.h"
#include "Array.h"
#include <string>
namespace rei::json {
class JsonObject;
class JsonArray;
enum FieldType {
Null,
Number,
Bool,
String,
Object,
Array
};
class FieldValue {
public:
FieldValue(const FieldValue& other) noexcept;
FieldValue& operator=(const FieldValue& other) noexcept;
FieldValue& operator=(FieldValue&& other) noexcept;
FieldValue(FieldValue&& other) noexcept;
FieldValue(FieldType type);
FieldValue(int value);
FieldValue(bool value);
FieldValue(const std::string& value);
FieldValue(std::string&& value);
FieldValue(const JsonObject& value);
FieldValue(JsonObject&& value);
FieldValue(const JsonArray& value);
FieldValue(JsonArray&& value) noexcept;
~FieldValue();
inline int asNumber() { return number; }
inline bool asBool() { return boolean; }
inline std::string& asString() { return string; }
inline JsonObject& asObject() { return object; }
inline JsonArray& asArray() { return array; }
inline bool isNumber() const { return type == FieldType::Number; }
inline bool isBool() const { return type == FieldType::Bool; }
inline bool isString() const { return type == FieldType::String; }
inline bool isObject() const { return type == FieldType::Object; }
inline bool isArray() const { return type == FieldType::Array; }
inline bool isNull() const { return type == FieldType::Null; }
private:
void _copyValues(const FieldValue& other);
void _moveValues(FieldValue&& other);
void _destroy();
FieldType type;
union {
int number;
bool boolean;
std::string string;
JsonObject object;
JsonArray array;
};
};
}

44
include/rei-json/Object.h Normal file
View file

@ -0,0 +1,44 @@
#pragma once
#include <functional>
#include <map>
#include <string>
namespace rei::json {
class JsonArray;
class FieldValue;
class JsonObject {
public:
JsonObject();
bool contains(const std::string& key) const;
JsonObject& set(const std::string& key, int value);
JsonObject& set(const std::string& key, bool value);
JsonObject& set(const std::string& key, const std::string& value);
JsonObject& set(const std::string& key, const std::string&& value);
JsonObject& set(const std::string& key, const char * value);
JsonObject& setNull(const std::string& key);
JsonObject& addArray(const std::string& key, const JsonArray& array);
JsonObject& addArray(const std::string& key, JsonArray&& array);
JsonObject& addObject(const std::string& key, const JsonObject& object);
JsonObject& addObject(const std::string& key, JsonObject&& object);
int getNumber(const std::string& key);
bool getBool(const std::string& key);
std::string getString(const std::string& key);
JsonObject& getObject(const std::string& key);
JsonArray& getArray(const std::string& key);
bool hasNull(const std::string& key);
void forEach(std::function<void(FieldValue&, const std::string& key)>);
size_t count() const;
private:
std::map<std::string, FieldValue> elements;
};
}

14
include/rei-json/json.h Normal file
View file

@ -0,0 +1,14 @@
#pragma once
#include "Object.h"
#include "Array.h"
#include "Field.h"
#include <string>
#include <variant>
namespace rei::json {
class std::variant<JsonObject, JsonArray> parse(const std::string& jsonStr);
std::string toString(std::variant<JsonObject, JsonArray>& element);
std::string toString(JsonObject& object);
std::string toString(JsonArray& object);
}

115
src/Array.cpp Normal file
View file

@ -0,0 +1,115 @@
#include "rei-json/Field.h"
#include "rei-json/Array.h"
#include "rei-json/Object.h"
#include <print>
#include <stdexcept>
#include <string>
namespace rei::json {
JsonArray::JsonArray() {
elements.reserve(1000);
}
JsonArray& JsonArray::push(int value) {
elements.push_back(value);
return *this;
}
JsonArray& JsonArray::push(bool value) {
elements.push_back(value);
return *this;
}
JsonArray& JsonArray::push(const char * value) {
elements.push_back(std::string(value));
return *this;
}
JsonArray& JsonArray::push(const std::string& value) {
elements.push_back(value);
return *this;
}
JsonArray& JsonArray::push(const std::string&& value) {
elements.push_back(std::move(value));
return *this;
}
JsonArray& JsonArray::pushNull() {
elements.push_back(std::move(FieldValue{FieldType::Null}));
return *this;
}
JsonArray& JsonArray::pushObject(const JsonObject& object) {
elements.push_back(object);
return *this;
};
JsonArray& JsonArray::pushObject(const JsonObject&& object) {
elements.push_back(std::move(object));
return *this;
};
JsonArray& JsonArray::pushArray(const JsonArray& array) {
elements.push_back(array);
return *this;
};
JsonArray& JsonArray::pushArray(const JsonArray&& array) {
elements.push_back(std::move(array));
return *this;
};
int JsonArray::getNumber(int index) {
if (index >= elements.size()) {
throw std::logic_error(std::string("Trying to access non existing index: ") + std::to_string(index));
}
return elements[index].asNumber();;
}
bool JsonArray::getBool(int index) {
if (index >= elements.size()) {
throw std::logic_error(std::string("Trying to access non existing index: ") + std::to_string(index));
}
return elements[index].asBool();;
}
std::string JsonArray::getString(int index) {
if (index >= elements.size()) {
throw std::logic_error(std::string("Trying to access non existing index: ") + std::to_string(index));
}
return elements[index].asString();
}
JsonObject& JsonArray::getObject(int index) {
if (index >= elements.size()) {
throw std::logic_error(std::string("Trying to access non existing index: ") + std::to_string(index));
}
return elements[index].asObject();
}
JsonArray& JsonArray::getArray(int index) {
if (index >= elements.size()) {
throw std::logic_error(std::string("Trying to access non existing index: ") + std::to_string(index));
}
return elements[index].asArray();
}
bool JsonArray::hasNull(int index) {
if (index >= elements.size()) {
throw std::logic_error(std::string("Trying to access non existing index: ") + std::to_string(index));
}
return elements[index].isNull();
}
void JsonArray::forEach(std::function<void(FieldValue&, int)> func) {
for (int i = 0; i < elements.size(); ++i) {
func(elements[i], i);
}
}
size_t JsonArray::count() const {
return elements.size();
}
}

127
src/Field.cpp Normal file
View file

@ -0,0 +1,127 @@
#include "rei-json/Field.h"
#include <algorithm>
#include <ostream>
#include <print>
#include <utility>
namespace rei::json {
FieldValue::FieldValue(const FieldValue& other) noexcept {
//std::println("I'm being copied ! (constructor) {} {}", static_cast<int>(other.type), other.number);
_copyValues(std::move(other));
}
FieldValue& FieldValue::operator=(const FieldValue& other) noexcept {
//std::println("I'm being copied ! (operator) {}", static_cast<int>(other.type));
_destroy();
_copyValues(std::move(other));
return *this;
}
FieldValue& FieldValue::operator=(FieldValue&& other) noexcept {
//std::println("I'm being moved ! (operator) {} {}", static_cast<int>(other.type), other.number);
_destroy();
_moveValues(std::move(other));
return *this;
}
FieldValue::FieldValue(FieldValue&& other) noexcept {
//std::println("I'm being moved ! (constructor) {} {}", static_cast<int>(other.type), other.number);
_moveValues(std::move(other));
}
void FieldValue::_copyValues(const FieldValue& other) {
switch (other.type) {
case rei::json::FieldType::Number:
number = other.number;
break;
case rei::json::FieldType::Bool:
boolean = other.boolean;
break;
case rei::json::FieldType::String:
new (&string) std::string(other.string);
break;
case rei::json::FieldType::Object:
new (&object) JsonObject(other.object);
break;
case rei::json::FieldType::Array:
new (&array) JsonArray(other.array);
break;
}
type = other.type;
}
void FieldValue::_moveValues(FieldValue&& other) {
switch (other.type) {
case rei::json::FieldType::Number:
number = other.number;
break;
case rei::json::FieldType::Bool:
boolean = other.boolean;
break;
case rei::json::FieldType::String:
new (&string) std::string(std::move(other.string));
break;
case rei::json::FieldType::Object:
new (&object) JsonObject(std::move(other.object));
break;
case rei::json::FieldType::Array:
new (&array) JsonArray(std::move(other.array));
break;
}
type = std::move(other.type);
}
FieldValue::FieldValue(FieldType type) : number(0), type(type) {
}
FieldValue::FieldValue(int value) : number(value), type(FieldType::Number) {
}
FieldValue::FieldValue(bool value) : boolean(value), type(FieldType::Bool) {
}
FieldValue::FieldValue(const std::string& value) : string(value), type(FieldType::String) {
}
FieldValue::FieldValue(std::string&& value) : string(std::move(value)), type(FieldType::String) {
}
FieldValue::FieldValue(const JsonObject& value) : object(value), type(FieldType::Object) {
}
FieldValue::FieldValue(JsonObject&& value) : object(std::move(value)), type(FieldType::Object) {
}
FieldValue::FieldValue(const JsonArray& value) : array(value), type(FieldType::Array) {
//std::println("Array ! copy");
}
FieldValue::FieldValue(JsonArray&& value) noexcept : array(std::move(value)), type(FieldType::Array) {
//std::println("Array ! move");
}
FieldValue::~FieldValue() {
_destroy();
}
void FieldValue::_destroy() {
if (isString()) {
(&string)->std::string::~string();
} else if (isObject()) {
object.~JsonObject();
} else if (isArray()) {
array.~JsonArray();
}
}
}

119
src/Object.cpp Normal file
View file

@ -0,0 +1,119 @@
#include "rei-json/json.h"
#include "rei-json/Field.h"
#include "rei-json/Array.h"
#include <print>
#include <stdexcept>
#include <string>
#include <utility>
namespace rei::json {
JsonObject::JsonObject() {
};
bool JsonObject::contains(const std::string& key) const {
return elements.contains(key);
}
JsonObject& JsonObject::set(const std::string& key, int value) {
elements.insert_or_assign(key, value);
return *this;
}
JsonObject& JsonObject::set(const std::string& key, bool value) {
elements.insert_or_assign(key, value);
return *this;
}
JsonObject& JsonObject::set(const std::string& key, const std::string& value) {
elements.insert_or_assign(key, value);
return *this;
}
JsonObject& JsonObject::set(const std::string& key, const std::string&& value) {
elements.insert_or_assign(key, std::move(value));
return *this;
}
JsonObject& JsonObject::setNull(const std::string& key) {
elements.insert_or_assign(key, std::move(FieldValue{FieldType::Null}));
return *this;
}
JsonObject& JsonObject::set(const std::string& key, const char * value) {
elements.insert_or_assign(key, std::string(value));
return *this;
}
int JsonObject::getNumber(const std::string& key) {
if (!contains(key)) {
throw std::logic_error(std::string("Trying to access non existing key: ") + key);
}
return elements.at(key).asNumber();
}
bool JsonObject::getBool(const std::string& key) {
if (!contains(key)) {
throw std::logic_error(std::string("Trying to access non existing key: ") + key);
}
return elements.at(key).asBool();
}
std::string JsonObject::getString(const std::string& key) {
if (!contains(key)) {
throw std::logic_error(std::string("Trying to access non existing key: ") + key);
}
return elements.at(key).asString();
}
JsonObject& JsonObject::getObject(const std::string& key) {
if (!contains(key)) {
throw std::logic_error(std::string("Trying to access non existing key: ") + key);
}
return elements.at(key).asObject();
}
JsonArray& JsonObject::getArray(const std::string& key) {
if (!contains(key)) {
throw std::logic_error(std::string("Trying to access non existing key: ") + key);
}
return elements.at(key).asArray();
}
bool JsonObject::hasNull(const std::string& key) {
if (!contains(key)) {
throw std::logic_error(std::string("Trying to access non existing key: ") + key);
}
return elements.at(key).isNull();
}
JsonObject& JsonObject::addArray(const std::string& key, const JsonArray& array) {
elements.insert_or_assign(key, array);
return *this;
}
JsonObject& JsonObject::addArray(const std::string& key, JsonArray&& array) {
elements.insert_or_assign(key, std::move(array));
return *this;
}
JsonObject& JsonObject::addObject(const std::string& key, const JsonObject& object) {
elements.insert_or_assign(key, object);
return *this;
}
JsonObject& JsonObject::addObject(const std::string& key, JsonObject&& object) {
elements.insert_or_assign(key, std::move(object));
return *this;
}
void JsonObject::forEach(std::function<void(FieldValue&, const std::string& key)> func) {
for (auto& element : elements) {
func(element.second, element.first);
}
}
size_t JsonObject::count() const {
return elements.size();
}
}

202
src/parse.cpp Normal file
View file

@ -0,0 +1,202 @@
#include "rei-json/Array.h"
#include "rei-json/Field.h"
#include "rei-json/json.h"
#include <algorithm>
#include <cctype>
#include <charconv>
#include <chrono>
#include <memory>
#include <ostream>
#include <print>
#include <stdexcept>
#include <string>
#include <string_view>
#include <variant>
namespace rei::json {
void goToNextDoubleQuote(const std::string& str, int* const i) {
while (str[*i] != '"') {
if (str[*i] == '\\') {
++(*i);
}
++(*i);
}
}
void skipDigits(const std::string& str, int* const i) {
while (isdigit(str[*i]) || str[*i] == '-') {
++(*i);
}
}
void skipWhiteSpaces(const std::string& str, int* const i) {
while (str[*i] == ' ' || str[*i] == '\t' || str[*i] == '\n' || str[*i] == '\v') {
++(*i);
}
}
void parseObject(const std::string &str, int *i, JsonObject* object);
void parseArray(const std::string &str, int *i, JsonArray* object);
void parseObjectField(const std::string &str, int *i, JsonObject* object) {
if (str[*i] != '"') {
throw "wrong json: expected opening quote";
}
++(*i);
const int keyStart = *i;
goToNextDoubleQuote(str, i);
const int keyLength = *i - keyStart;
const std::string key = str.substr(keyStart, keyLength);
++(*i);
skipWhiteSpaces(str, i);
if (str[*i] != ':') {
throw "wrong json: expect 2 dots";
}
++(*i);
skipWhiteSpaces(str, i);
if (str[*i] == '"') {
++(*i);
const int valueStart = *i;
goToNextDoubleQuote(str, i);
const int valueLength = *i - valueStart;
const std::string valueStr = str.substr(valueStart, valueLength);
object->set(key, std::move(valueStr));
++(*i);
} else if (isdigit(str[*i]) || str[*i] == '-') {
const int valueStart = *i;
skipDigits(str, i);
const int valueLength = *i - valueStart;
const std::string valueStr = str.substr(valueStart, valueLength);
int value;
std::from_chars(str.c_str() + valueStart, str.c_str() + valueStart + valueLength, value);
object->set(key, value);
} else if (std::string_view{str}.substr(*i, 4) == "true") {
const int valueStart = *i;
*i += 4;
const int valueLength = *i - valueStart;
object->set(key, true);
} else if (std::string_view{str}.substr(*i, 5) == "false") {
const int valueStart = *i;
*i += 5;
const int valueLength = *i - valueStart;
object->set(key, false);
} else if (std::string_view{str}.substr(*i, 4) == "null") {
*i += 4;
object->setNull(key);
} else if (str[*i] == '{'){
JsonObject newObject{};
parseObject(str, i, &newObject);
object->addObject(key, std::move(newObject));
} else if (str[*i] == '['){
JsonArray newArray{};
parseArray(str, i, &newArray);
object->addArray(key, std::move(newArray));
}
}
void parseArrayField(const std::string &str, int *i, JsonArray* array) {
skipWhiteSpaces(str, i);
if (str[*i] == '"') {
++(*i);
const int valueStart = *i;
goToNextDoubleQuote(str, i);
const int valueLength = *i - valueStart;
const std::string valueStr = str.substr(valueStart, valueLength);
array->push(std::move(valueStr));
++(*i);
} else if (isdigit(str[*i])) {
const int valueStart = *i;
skipDigits(str, i);
const int valueLength = *i - valueStart;
const std::string valueStr = str.substr(valueStart, valueLength);
array->push(std::stoi(valueStr));
} else if (std::string_view{str}.substr(*i, 4) == "true") {
const int valueStart = *i;
*i += 4;
const int valueLength = *i - valueStart;
array->push(true);
} else if (std::string_view{str}.substr(*i, 4) == "null") {
*i += 4;
array->pushNull();
} else if (std::string_view{str}.substr(*i, 5) == "false") {
const int valueStart = *i;
*i += 5;
const int valueLength = *i - valueStart;
array->push(false);
} else if (str[*i] == '{'){
JsonObject newObject{};
parseObject(str, i, &newObject);
array->pushObject(std::move(newObject));
} else if (str[*i] == '['){
JsonArray newArray{};
parseArray(str, i, &newArray);
array->pushArray(std::move(newArray));
}
}
void parseArray(const std::string &str, int *i, JsonArray* object) {
if (str[*i] != '[') {
throw "wrong json";
}
++(*i);
skipWhiteSpaces(str, i);
while (str[*i] != ']') {
parseArrayField(str, i, object);
skipWhiteSpaces(str, i);
if (str[*i] == ',') {
++(*i);
skipWhiteSpaces(str, i);
if (str[*i] == ']') {
throw "wrong json: extra comma on last array field";
}
} else if (str[*i] != ']') {
throw "wrong json: expected array end";
}
}
++(*i);
}
void parseObject(const std::string &str, int *i, JsonObject* object) {
if (str[*i] != '{') {
throw "wrong json";
}
++(*i);
skipWhiteSpaces(str, i);
while (str[*i] != '}') {
if (str[*i] != '"') {
throw "wrong json: expect opening key quote";
}
parseObjectField(str, i, object);
skipWhiteSpaces(str, i);
if (str[*i] == ',') {
++(*i);
skipWhiteSpaces(str, i);
if (str[*i] == '}') {
throw "wrong json: extra comma on last object field";
}
} else if (str[*i] != '}') {
const std::string sstr = str.substr(*i - 1);
throw "wrong json: expected object end";
}
}
++(*i);
}
std::variant<JsonObject, JsonArray> parse(const std::string& jsonStr) {
int i = 0;
skipWhiteSpaces(jsonStr, &i);
if (jsonStr[i] == '{') {
JsonObject object{};
parseObject(jsonStr, &i, &object);
return std::move(object);
} else if (jsonStr[i] == '[') {
JsonArray array{};
parseArray(jsonStr, &i, &array);
return std::move(array);
}
throw std::runtime_error("Bad json syntax");
}
}

103
src/toString.cpp Normal file
View file

@ -0,0 +1,103 @@
#include "rei-json/Array.h"
#include "rei-json/Field.h"
#include "rei-json/Object.h"
#include "rei-json/json.h"
#include <cctype>
#include <print>
#include <stdexcept>
#include <string>
#include <variant>
namespace rei::json {
void arrayToString(JsonArray& array, std::string& str, int currentIndentation, int indentIncrement);
inline void objectToString(JsonObject& object, std::string& str, int currentIndentation, int indentIncrement) {
int elementCount = object.count();
//std::string str = "{";
str += "{";
currentIndentation += indentIncrement;
std::string padding = "\n" + std::string(currentIndentation, ' ');
object.forEach([&](FieldValue& element, const std::string& key) {
str += padding + "\"" + key + "\": ";
if (element.isNumber()) {
str += std::to_string(element.asNumber());
} else if (element.isBool()) {
str += element.asBool() ? "true" : "false";
} else if (element.isNull()) {
str += "null";
} else if (element.isString()) {
str += "\"" + element.asString() + "\"";
} else if (element.isObject()) {
objectToString(element.asObject(), str, currentIndentation, indentIncrement);
} else if (element.isArray()) {
arrayToString(element.asArray(), str, currentIndentation, indentIncrement);
}
elementCount--;
if (elementCount > 0) {
str += ",";
}
});
currentIndentation -= indentIncrement;
if (object.count() > 0) {
str += "\n" + std::string(currentIndentation, ' ');
}
str += "}";
//return str;
}
inline void arrayToString(JsonArray& array, std::string& str, int currentIndentation, int indentIncrement) {
int elementCount = array.count();
//std::string str = "[";
str += "[";
currentIndentation += indentIncrement;
std::string padding = "\n" + std::string(currentIndentation, ' ');
array.forEach([&](FieldValue& element, int index) {
str += padding;
if (element.isNumber()) {
str += std::to_string(element.asNumber());
} else if (element.isBool()) {
str += element.asBool() ? "true" : "false";
} else if (element.isNull()) {
str += "null";
} else if (element.isString()) {
str += "\"" + element.asString() + "\"";
} else if (element.isObject()) {
objectToString(element.asObject(), str, currentIndentation, indentIncrement);
} else if (element.isArray()) {
arrayToString(element.asArray(), str, currentIndentation, indentIncrement);
}
--elementCount;
if (elementCount > 0) {
str += ",";
}
});
currentIndentation -= indentIncrement;
if (array.count() > 0) {
str += "\n" + std::string(currentIndentation, ' ');
}
str += "]";
//return str;
}
std::string toString(std::variant<JsonObject, JsonArray>& element) {
if (std::holds_alternative<JsonObject>(element)) {
return toString(std::get<JsonObject>(element));
} else if (std::holds_alternative<JsonArray>(element)) {
return toString(std::get<JsonArray>(element));
}
throw std::runtime_error("Bad json format");
}
std::string toString(JsonObject& object) {
std::string str;
objectToString(object, str, 0, 4);
return str;
}
std::string toString(JsonArray& array) {
std::string str;
arrayToString(array, str, 0, 4);
return str;
}
}

50
tests/parsing.cpp Normal file
View file

@ -0,0 +1,50 @@
#include "rei-json/Object.h"
#include "rei-json/json.h"
#include <catch2/catch_test_macros.hpp>
#include <string>
TEST_CASE("Parsing json object") {
std::string jsonStr = R"({
"keyArray": [
42,
"elemString",
"",
true,
false,
null
],
"keyBooleanFalse": false,
"keyBooleanTrue": true,
"keyEmptyString": "",
"keyNegativeNumber": -13,
"keyNull": null,
"keyObject": {
"keyNumberOnObject": 42
},
"keyPositiveNumber": 12,
"keyString": "YEP"
})";
auto json = rei::json::parse(jsonStr);
auto& objectJson = std::get<rei::json::JsonObject>(json);
REQUIRE(objectJson.getNumber("keyPositiveNumber") == 12);
REQUIRE(objectJson.getNumber("keyNegativeNumber") == -13);
REQUIRE(objectJson.getBool("keyBooleanTrue") == true);
REQUIRE(objectJson.getBool("keyBooleanFalse") == false);
REQUIRE(objectJson.getString("keyString") == "YEP");
REQUIRE(objectJson.getString("keyEmptyString") == "");
REQUIRE(objectJson.getObject("keyObject").getNumber("keyNumberOnObject") == 42);
REQUIRE(objectJson.hasNull("keyNull") == true);
REQUIRE(objectJson.hasNull("keyPositiveNumber") == false);
REQUIRE_THROWS(objectJson.getNumber("nonExistantKey"));
auto& arrayJson = objectJson.getArray("keyArray");
REQUIRE(arrayJson.getNumber(0) == 42);
REQUIRE(arrayJson.getString(1) == "elemString");
REQUIRE(arrayJson.getString(2) == "");
REQUIRE(arrayJson.getBool(3) == true);
REQUIRE(arrayJson.getBool(4) == false);
REQUIRE(arrayJson.hasNull(5));
REQUIRE_THROWS(arrayJson.getNumber(99));
}

54
tests/stringify.cpp Normal file
View file

@ -0,0 +1,54 @@
#include "rei-json/json.h"
#include <catch2/catch_test_macros.hpp>
#include <string>
TEST_CASE("Stringify json") {
auto objectJson = rei::json::JsonObject{};
objectJson
.set("keyPositiveNumber", 12)
.set("keyNegativeNumber", -13)
.set("keyBooleanTrue", true)
.set("keyBooleanFalse", false)
.set("keyString", "YEP")
.set("keyEmptyString", "")
.setNull("keyNull");
rei::json::JsonObject obj{};
obj.set("keyNumberOnObject", 42);
rei::json::JsonArray array{};
array.push(42);
array.push("elemString");
array.push("");
array.push(true);
array.push(false);
array.pushNull();
objectJson.addObject("keyObject", std::move(obj));
objectJson.addArray("keyArray", std::move(array));
std::string expectedJson = R"({
"keyArray": [
42,
"elemString",
"",
true,
false,
null
],
"keyBooleanFalse": false,
"keyBooleanTrue": true,
"keyEmptyString": "",
"keyNegativeNumber": -13,
"keyNull": null,
"keyObject": {
"keyNumberOnObject": 42
},
"keyPositiveNumber": 12,
"keyString": "YEP"
})";
REQUIRE(rei::json::toString(objectJson) == expectedJson);
}

51
tests/usage.cpp Normal file
View file

@ -0,0 +1,51 @@
#include "rei-json/json.h"
#include <catch2/catch_test_macros.hpp>
#include <print>
#include <string>
#include <utility>
TEST_CASE("Basic json") {
auto objectJson = rei::json::JsonObject{};
objectJson
.set("keyPositiveNumber", 12)
.set("keyNegativeNumber", -13)
.set("keyBooleanTrue", true)
.set("keyBooleanFalse", false)
.set("keyString", "YEP")
.set("keyEmptyString", "")
.setNull("keyNull");
rei::json::JsonObject obj{};
obj.set("keyNumberOnObject", 42);
rei::json::JsonArray array{};
array.push(42);
array.push("elemString");
array.push("");
array.push(true);
array.push(false);
array.pushNull();
objectJson.addObject("keyObject", std::move(obj));
objectJson.addArray("keyArray", std::move(array));
REQUIRE(objectJson.getNumber("keyPositiveNumber") == 12);
REQUIRE(objectJson.getNumber("keyNegativeNumber") == -13);
REQUIRE(objectJson.getBool("keyBooleanTrue") == true);
REQUIRE(objectJson.getBool("keyBooleanFalse") == false);
REQUIRE(objectJson.getString("keyString") == "YEP");
REQUIRE(objectJson.getString("keyEmptyString") == "");
REQUIRE(objectJson.getObject("keyObject").getNumber("keyNumberOnObject") == 42);
REQUIRE(objectJson.hasNull("keyNull") == true);
REQUIRE(objectJson.hasNull("keyPositiveNumber") == false);
REQUIRE_THROWS(objectJson.getNumber("nonExistantKey"));
auto& arrayJson = objectJson.getArray("keyArray");
REQUIRE(arrayJson.getNumber(0) == 42);
REQUIRE(arrayJson.getString(1) == "elemString");
REQUIRE(arrayJson.getString(2) == "");
REQUIRE(arrayJson.getBool(3) == true);
REQUIRE(arrayJson.getBool(4) == false);
REQUIRE(arrayJson.hasNull(5));
REQUIRE_THROWS(arrayJson.getNumber(99));
}