simjson - the simplest library for working with JSON

Designed to work with JSON when using the simstr library.
Version 1.2.1.
This library contains a simple implementation of a simple JsonValue object for working with JSON using string objects of the simstr library, since other libraries mainly work with std::string or raw const char*. The task was not to somehow compete in performance or optimality with other libraries, I mainly use it for working with small config files - read, modify, write.
For json objects, std::unordered_map is used, in the form of hashStrMap<K, JsonValueTemp<K>>, for arrays - std::vector<JsonValueTemp<K>>, strings are stored in sstring<K>.
Key features of the library
- Works with all simstr strings.
- Supports working with strings
char, char16_t, char32_t, wchar_t.
- Convenient creation, reading and modification of json values.
- Copying JSON values such as arrays and objects is done by reference (only
shared_ptr is copied).
- Possible "deep" copying aka cloning of JSON values, in this case a full copy is created for arrays and objects.
- "Merging" one JSON object with another, with the ability to set priority.
- Extended work with numbers - allows you to use int64_t and double.
- Parsing a string into Json, with support for partial parsing.
- Serializing json to a string, with options - sorting keys, "readable" output, number of indents and symbol indentation with "readable" output.
Main objects of the library
- JsonValueTempl<K> - Json value type, parameter K specifies the type of characters used in the string. Aliases:
- JsonValue - for char strings
- JsonValueU - for char16_t strings
- JsonValueUU - for char32_t strings
- JsonValueW - for wchar_t strings
- StreamedJsonParser<K> - parser of a string into JSON, supporting "partial" parsing. For example, data comes in portions from the network, you feed it to the parser as it arrives, until it either parses, or throws an error.
Usage
simjson consists of a header file and one source file. You can connect as a CMake project via add_subdirectory (the simjson library), you can simply include the files in your project. Building also requires simstr (when using CMake downloads automatically).
simjson requires a compiler with support for a standard no lower than C++20 (concepts are used).
Usage examples
Creating, reading
JsonValue json = 1;
stringa text = json.
store();
JsonValue obj = {
{"Key1"_h, 1},
{"Key2"_h, true},
{"Key3", {1, 2, Json::null, "test", false}},
{"Key4"_h, {
{"subkey1"_h, true},
{"subkey2", "subkey"},
}},
};
test["a"_h]["b"_h]["c"_h] = 10;
test[0] = "value";
test[-1] = true;
test[10] = false;
....
stringa log_path = config(
"instance"_h,
"base"_h,
"log"_h,
"path"_h).
text().value_or(
"./log.txt");
stringa work_dir = config("instance"_h, "base"_h, "work_dir"_h).text_or_throw<std::runtime_error>(
"not specified work dir");
std::optional< strType > text() const
Definition json.h:480
SIMJSON_API void store(lstring< K, 0, true > &stream, bool prettify=false, bool order_keys=false, K indent_symbol=' ', unsigned indent_count=2) const
Serialize json value to string.
Definition json.cpp:504
JsonValueTempl< u8s > JsonValue
Definition json.h:898
Example: Setting default values, reading from a file and merging with default values.
JsonValue json_config = {
{"section1"_h, {
{"sub1"_h, {
{"path"_h, "."},
{"workers"_h, 10},
}},
{"sub2"_h, {
{"check"_h, true},
}},
{"key1"_h, true},
{"key2"_h, false},
}},
{"section2"_h, {
{"key1"_h, false},
{"key2"_h, true},
}},
};
inline bool is_file_exist(stra path) {
auto status = std::filesystem::status(path.to_sv());
return std::filesystem::status_known(status) && std::filesystem::is_regular_file(status);
}
void read_config_from_file(ssa folder, ssa file_name) {
stringa path_checked = folder + e_if(folder(-1) != PATH_SEPARATOR, PATH_SEPARATOR);
if (!file_name.is_empty()) {
lstringa<MAX_PATH> fullPath = path_checked + file_name;
stringa config;
if (is_file_exist(fullPath)) {
auto [readed, error, line, col] = JsonValue::parse(config);
if (error != JsonParseResult::Success) {
std::cerr << "Error in parse config file " << fullPath <<
" at line " << line << ", col " << col << std::endl;
throw std::runtime_error{"Error parse config file"};
}
json_config.merge(readed);
}
stringa new_config = json_config.store(true, true, ' ', 4);
if (new_config != config) {
std::ofstream file(fullPath.c_str(), std::ios::binary | std::ios::trunc);
if (!file.is_open()) {
std::cerr << "Error in create config file " << fullPath << std::endl;
} else {
file.write(new_config.c_str(), new_config.length());
}
}
}
json_config["runtime"_h]["location"_h]["base_dir"_h] = std::move(path_checked);
}
SIMJSON_API stringa get_file_content(stra filePath)
Read the file into a line.
Definition json.cpp:1140
Example - initialization from standard containers
std::vector<int> vals = {1, 2, 3, 4};
JsonValue json = vals;
EXPECT_EQ(json.type(), Json::Array);
EXPECT_EQ(json.store(), "[1,2,3,4]");
JsonValue json1 = std::array<int, 4>{4, 3, 2, 1};
EXPECT_EQ(json1.type(), Json::Array);
EXPECT_EQ(json1.store(), "[4,3,2,1]");
std::list<stringa> texts = {"one", "two", "three"};
EXPECT_EQ(json2.type(), Json::Array);
EXPECT_EQ(json2.store(), "[\"one\",\"two\",\"three\"]");
hashStrMapA<int> vals = {
{"one"_h, 1}, {"two"_h, 2}, {"three"_h, 3}, {"four"_h, 4}
};
EXPECT_EQ(json.type(), Json::Object);
EXPECT_EQ(json.store(false, true), "{\"four\":4,\"one\":1,\"three\":3,\"two\":2}");
std::map<stringa, int> vals1 = {
{"one", 1}, {"two", 2}, {"three", 3}, {"four", 4}
};
EXPECT_EQ(json1.type(), Json::Object);
EXPECT_EQ(json1.store(false, true), "{\"four\":4,\"one\":1,\"three\":3,\"two\":2}");
std::vector<std::pair<lstringa<20>, int>> vals2 = {
{"one", 1}, {"two", 2}, {"three", 3}, {"four", 4}
};
EXPECT_EQ(json2.type(), Json::Object);
EXPECT_EQ(json2.store(false, true), "{\"four\":4,\"one\":1,\"three\":3,\"two\":2}");
Generated documentation
Located here