diff --git a/CMakeLists.txt b/CMakeLists.txt index aa85211..4f19d20 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -11,9 +11,9 @@ option(BUILD_TESTING "Build testing" ON) set(CMAKE_CXX_STANDARD 17) if(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC" OR CMAKE_CXX_COMPILER_FRONTEND_VARIANT STREQUAL "MSVC") - add_compile_options("/utf-8" "/W4" "/WX" "/Zc:preprocessor") + add_compile_options("/utf-8" "/W4" "/Zc:preprocessor") else() - add_compile_options("-Wall;-Wextra;-Wpedantic;-Werror;-mtune=native") + add_compile_options("-Wall;-Wextra;-Wpedantic;-mtune=native") if(CMAKE_CXX_COMPILER_ID MATCHES "Clang") add_compile_options("-Wno-gnu-zero-variadic-macro-arguments") endif() @@ -28,7 +28,6 @@ endif() if (BUILD_SAMPLE) add_executable(sample sample/sample.cpp include/json.hpp) - add_executable(json5_parse_sample sample/json5_parse.cpp include/json.hpp include/json5.hpp) endif() if (BUILD_TESTING) diff --git a/README.md b/README.md index ad9bbb5..b2358f3 100644 --- a/README.md +++ b/README.md @@ -2,9 +2,9 @@ # meojson -现代化的全平台 Json/Json5 解析/生成器,Header-only,并附带大量语法糖! +现代化的全平台 Json/Json5 解析/生成器,Header-only,并且使用了魔法! -A modern all-platform Json/Json5 parser/serializer, which is header-only and contains lots of syntactic sugar! +A modern all-platform Json/Json5 parser/serializer, which is header-only and used magic! @@ -14,32 +14,202 @@ A modern all-platform Json/Json5 parser/serializer, which is header-only and con - 在您的项目中包含头文件即可使用 -```cpp +```c++ #include "json.hpp" ``` - 若您需要解析 Json5, 则请包含 `json5.hpp` 头文件 -```cpp +```c++ #include "json5.hpp" ``` -- meojson 仅依赖 STL, 但需要 c++17 标准 +- **meojson** 仅依赖 STL, 但需要 c++17 标准 +- 若使用 MSVC,请在项目中添加 `/Zc:preprocessor` +- 若使用 AppleClang,请在项目中添加 `-Wno-gnu-zero-variadic-macro-arguments` -## 示例 +### 序列化 -### 解析 +以下是一些基本特性: -```cpp -/*** - * from sample/sample.cpp -***/ -#include -#include "json.hpp" +```c++ +json::value j; +j["pi"] = 3.14; +j["happy"] = true; +j["answer"]["everything"] = 42; +j["object"] = { {"currency", "USD"}, {"value", 42.99} }; +``` + +以及一些有趣的特性: + +```c++ +std::set set { 1, 2, 3 }; +j["set"] = set; + +// 什么鬼类型! +std::unordered_map>>> map { + { "key_1", { { { "inner_key_1", { 7, 8, 9 } } }, { { "inner_key_2", { 10 } } } } }, +}; +j["map"] = map; + +// output: +// {"answer":{"everything":42},"happy":true,"map":{"key_1":[{"inner_key_1":[7,8,9]},{"inner_key_2":[10]}]},"object":{"currency":"USD","value":42.990000},"pi":3.140000,"set":[1,2,3]} +std::cout << j << std::endl; +``` + +然后,别眨眼,我们又转回来了! -void parsing() +```c++ +double pi = (double)j["pi"]; +int answer = (int)j["answer"]["everything"]; + +std::set new_set = (std::set)j["set"]; +// 又是这个鬼类型 +auto new_map = (std::unordered_map>>>)j["map"]; +``` + +然而,对于运行时的 JSON,最好先检查它是否可以转换。 + +```c++ +if (j["happy"].is>()) { + std::vector vec = (std::vector)j["happy"]; +} +else { + std::cout << "`天啊, j[\"happy\"] 不是一个数组!" << std::endl; + std::cout << "`还好我检查了,不然就炸了!" << std::endl; +} +``` + +我猜你已经理解了,是的,**meojson** 不仅仅是一个 JSON 库,还是一个序列化库! + +```c++ +struct MyStruct { - std::string content = R"( + int x = 0; + std::vector vec; + // 怎么总是你! + std::unordered_map>>> map; + + // 我们加点魔法 + MEO_JSONIZATION(x, vec, map); +}; + +MyStruct mine; +mine.vec.emplace_back(0.5); +mine.map = { { "key_1", { { { "inner_key_1", { 7, 8, 9 } } }, { { "inner_key_2", { 10 } } } } } }; + +// 是的,它是那么直观和流畅! +json::value j_mine = mine; +// output: {"map":{"key_1":[{"inner_key_1":[7,8,9]},{"inner_key_2":[10]}]},"vec":[0.500000],"x":0} +std::cout << j_mine << std::endl; + +// 恰恰,我们也可以把它变回来! +MyStruct new_mine = (MyStruct)j_mine; +``` + +嵌套调用也是易如反掌的! + +```c++ +struct Outter +{ + int outter_a = 10; + std::vector my_vec; + + MEO_JSONIZATION(outter_a, my_vec); +}; + +Outter o; +o.my_vec.emplace_back(mine); +json::value j_o = o; +// output: {"my_vec":[{"map":{"key_1":[{"inner_key_1":[7,8,9]},{"inner_key_2":[10]}]},"vec":[0.500000],"x":0}],"outter_a":10} +std::cout << j_o << std::endl; + +// 同样的反序列化 +Outter new_o = (Outter)j_o; +``` + +对于可选字段,我们可以在其中添加 `MEO_OPT`,这样在转换时,如果此字段在 JSON 中不存在,它将被跳过。 + +```c++ +struct OptionalFields +{ + int a = 0; + double b = 0; + std::vector c; + + MEO_JSONIZATION(a, MEO_OPT b, MEO_OPT c); +}; + +json::value ja = { + { "a", 100 }, +}; +if (ja.is()) { + OptionalFields var = (OptionalFields)ja; + // output: 100 + std::cout << var.a << std::endl; +} +``` + +对于第三方不可侵入的类型,你需要实现 `to_json`, `check_json`, `from_json` + +```c++ +struct ThirdPartyStruct +{ + int a = 100; +}; + +json::value to_json(const ThirdPartyStruct& t) { return t.a; } +bool check_json(const json::value& j, const ThirdPartyStruct&) { return j.is_number(); } +bool from_json(const json::value& j, ThirdPartyStruct& out) { out.a = j.as_integer(); return true; } + +// 然后你可以将其用作 JSON +ThirdPartyStruct third; +json::value jthird = third; +ThirdPartyStruct new_third = (ThirdPartyStruct)jthird; + +// 或者添加到你的结构中 +struct Outter2 +{ + int outter_a = 10; + ThirdPartyStruct third; + + MEO_JSONIZATION(outter_a, my_vec, third); +}; +``` + +还有一些琐碎的特性: + +```c++ +// 通过 `emplace` 向数组或对象添加元素 +j["set"].emplace(10); +j["object"].emplace("key3", "value3"); + +// 合并两个数组 +j["set"] += json::array { 11, 12 }; + +// 合并两个对象 +j["object"] |= { + { "key4", 4 }, + { "key5", false }, +}; + +// 转为字符串 +std::string oneline = j.dumps(); +std::string format = j.dumps(4); + +// 保存到文件 +std::ofstream ofs("meo.json"); +ofs << j; +ofs.close(); + +``` + +### 解析 + +现在让我们谈谈解析 + +```c++ +std::string content = R"( { "repo": "meojson", "author": { @@ -55,249 +225,121 @@ void parsing() { "C_str": "you found me!" } ] } +})"; + +// 它是一个 std::optional +auto ret = json::parse(content); + +if (!ret) { + std::cerr << "解析失败" << std::endl; + return; } - )"; +json::value& value = *ret; - auto ret = json::parse(content); +// Output: meojson +std::cout << (std::string)value["repo"] << std::endl; - if (!ret) { - std::cerr << "Parsing failed" << std::endl; - return; - } - json::value& value = ret.value(); // you can use rvalues if needed, like - // `auto value = std::move(ret).value();` - // Output: meojson - std::cout << value["repo"].as_string() << std::endl; - - /* Output: - ChingCdesu's homepage: https://github.com/ChingCdesu - MistEO's homepage: https://github.com/MistEO - */ - for (auto&& [name, homepage] : value["author"].as_object()) { - std::cout << name << "'s homepage: " << homepage.as_string() << std::endl; - } +/* Output: + ChingCdesu's homepage: https://github.com/ChingCdesu + MistEO's homepage: https://github.com/MistEO +*/ +for (auto&& [name, homepage] : (json::object)value["author"]) { + std::cout << name << "'s homepage: " << (std::string)homepage << std::endl; +} +// num = 3.141600 +double num = (double)value["num"]; - // Output: abc - std::string str = (std::string)value["str"]; // it is equivalent to `value["str"].as_string()` - std::cout << str << std::endl; - - // Output: 3.141600 - double num = value["num"].as_double(); // similarly, you can use `(double)value["num"]` - std::cout << num << std::endl; - - // Output: default_value - std::string get = value.get("maybe_exists", "default_value"); - std::cout << get << std::endl; - - // Output: you found me! - std::string nested_get = value.get("A_obj", "B_arr", 1, "C_str", "default_value"); - std::cout << nested_get << std::endl; - - // Output: 1, 2, 3 - // If the "list" is not an array or not exists, it will be a invalid optional; - auto opt = value.find("list"); - if (opt) { - auto& arr = opt.value(); - for (auto&& elem : arr) { - std::cout << elem.as_integer() << std::endl; - } - } - // more examples, it will output 3.141600 - auto opt_n = value.find("num"); - if (opt_n) { - std::cout << opt_n.value() << std::endl; - } - // If you use the `find` without template argument, it will return a `std::optional` - auto opt_v = value.find("not_exists"); - std::cout << "Did we find the \"not_exists\"? " << opt_v.has_value() << std::endl; +// get_value = "default_value" +std::string get_value = value.get("maybe_exists", "default_value"); +std::cout << get_value << std::endl; +``` - bool is_vec = value["list"].is>(); +和大多数解析库一样,这很无聊,你肯定不想看这个。 +所以让我给你看点有趣的东西。 - std::vector to_vec = value["list"].as_collection(); - to_vec = (std::vector)value["list"]; // same as above - to_vec = value["list"].as>(); // same as above +```c++ +// 多么神奇的 `get`,你可以连续输入 key 或 pos! +// nested_get = you found me! +std::string nested_get = value.get("A_obj", "B_arr", 1, "C_str", "default_value"); - // Output: 1, 2, 3 - for (auto&& i : to_vec) { - std::cout << i << std::endl; - } - std::list to_list = value["list"].as_collection(); - to_list = (std::list)value["list"]; // same as above - to_list = value["list"].as>(); // same as above +// `find` 可以帮助你找到并检查类型是否正确 +// 如果没有 `num`,则 opt_n 将为 std::nullopt +auto opt_n = value.find("num"); +if (opt_n) { + // 输出: 3.141600 + std::cout << *opt_n << std::endl; +} + +``` - std::set to_set = value["list"].as_collection(); - to_set = (std::set)value["list"]; // same as above - to_set = value["list"].as>(); // same as above - - bool is_map = value["author"].is>(); +还有一些你在序列化中已经见过的技巧 - std::map to_map = value["author"].as_map(); - to_map = (std::map)value["author"]; // same as above - to_map = value["author"].as>(); // same as above +```c++ +bool is_vec = value["list"].is>(); - auto to_hashmap = value["author"].as_map(); - to_hashmap = (std::unordered_map)value["author"]; // same as above - to_hashmap = value["author"].as>();// same as above +std::vector to_vec = value["list"].as_collection(); +to_vec = (std::vector)value["list"]; // 与上面相同 +to_vec = value["list"].as>(); // 与上面相同 - // Output: "literals" - using namespace json::literals; - auto val = "{\"hi\":\"literals\"}"_json; - std::cout << val["hi"] << std::endl; +// 输出: 1, 2, 3 +for (auto&& i : to_vec) { + std::cout << i << std::endl; } -``` -### 解析 Json5 +std::list to_list = value["list"].as_collection(); +to_list = (std::list)value["list"]; // 与上面相同 +to_list = value["list"].as>(); // 与上面相同 -```cpp -/*** - * from sample/json5_parse.cpp -***/ -#include -#include "json5.hpp" +std::set to_set = value["list"].as_collection(); +to_set = (std::set)value["list"]; // 与上面相同 +to_set = value["list"].as>(); // 与上面相同 -void parsing() -{ - std::string_view content = R"( -// 这是一段json5格式的信息 -{ - 名字: "MistEO", /* key的引号可省略 */ - 😊: '😄', // emoji为key - thanks: 'ありがとう', /* 单引号也可以表示字符串 */ - \u006Bey: ['value',], // 普通字符和转义可以混用 - inf: +Infinity, nan: NaN, // 数字可以以"+"开头 - fractional: .3, integer: 42., // 小数点作为起始/结尾 - byte_max: 0xff, // 十六进制数 - light_speed: +3e8, // 科学计数法 -} -)"; - auto ret = json::parse5(content); - if (!ret) { - std::cerr << "Parsing failed" << std::endl; - return; - } - json::value& value = ret.value(); // you can use rvalues if needed, like - // `auto value = std::move(ret).value();` - - // Output: MistEO - std::cout << value["名字"] << std::endl; - // Output: value - std::string str = (std::string)value["key"][0]; - std::cout << str << std::endl; - - // for more json::value usage, please refer to sample.cpp -} -``` +bool is_map = value["author"].is>(); -### 生成 +std::map to_map = value["author"].as_map(); +to_map = (std::map)value["author"]; // 与上面相同 +to_map = value["author"].as>(); // 与上面相同 -```cpp -/*** - * from sample/sample.cpp -***/ -#include -#include "json.hpp" +auto to_hashmap = value["author"].as_map(); +to_hashmap = (std::unordered_map)value["author"]; // 与上面相同 +to_hashmap = value["author"].as>();// 与上面相同 +``` -void serializing() -{ - json::value root; - - root["hello"] = "meojson"; - root["Pi"] = 3.1416; - - root["obj"] = { - { "obj_key1", "Hi" }, - { "obj_key2", 123 }, - { "obj_key3", true }, - }; - root["obj"].emplace("obj_key4", 789); - - root["obj"].emplace("obj_key5", json::object { { "key4 child", "i am object value" } }); - root["another_obj"]["child"]["grand"] = "i am grand"; - - // take union - root["obj"] |= json::object { - { "obj_key6", "i am string" }, - { "obj_key7", json::array { "i", "am", "array" } }, - }; - - root["arr"] = json::array { 1, 2, 3 }; - root["arr"].emplace(4); - root["arr"].emplace(5); - root["arr"] += json::array { 6, 7 }; - - std::vector vec = { 1, 2, 3, 4, 5 }; - root["arr from vec"] = vec; - - std::set set = { "a", "bb\n\nb", "cc\t" }; - root["arr from set"] = set; - - std::map map { - { "key1", 1 }, - { "key2", 2 }, - }; - root["obj from map"] = map; - - std::vector>> complex { { { 1, 2, 3 }, { 4, 5 } }, { { 6 }, { 7, 8 } } }; - root["complex"] = json::serialize(complex); - - std::map>> more_complex { - { "key1", { { 1, { 0.1, 0.2 } }, { 2, { 0.2, 0.3 } } } }, - { "key2", { { 3, { 0.4 } }, { 4, { 0.5, 0.6, 0.7 } } } }, - }; - // the "std::map" cannot be converted to json because the key is "int", - // you can set the template parameter "loose" of "serialize" to true, which will make a more relaxed conversion. - root["more_complex"] = json::serialize(more_complex); - - std::cout << root << std::endl; - - std::ofstream ofs("meo.json"); - ofs << root; - ofs.close(); -} +还有一些不知道有啥用的字面语法 + +```c++ +// Output: "literals" +using namespace json::literals; +auto val = "{\"hi\":\"literals\"}"_json; +std::cout << val["hi"] << std::endl; ``` -### 序列化 +但好消息是,我们也可以解析 JSON5! ```c++ -// 如果使用 MSVC, 请添加 "/Zc:preprocessor" 到项目配置中 -// 如果使用 AppleClang, 请添加 "-Wno-gnu-zero-variadic-macro-arguments" 到项目配置中 -void test_jsonization() +std::string_view content5 = R"( +// 这是一个 Json5 内容 { - struct MyStruct - { - std::vector vec; - std::map map; - int i = 0; - double d = 0; - - - // MEO_OPT 表示该变量是一个可选项 - // 即使输入中不存在该字段依然可以读取 - MEO_JSONIZATION(vec, map, MEO_OPT i, d); - }; - - MyStruct a; - a.vec = { 1, 2, 3 }; - a.map = { { "key", 5 } }; - a.i = 100; - a.d = 0.5; - - json::value dumps = a; - - // output: { "d" : 0.500000, "i" : 100, "map" : { "key" : 5 }, "vec" : [ 1, 2, 3 ] } - std::cout << dumps << std::endl; - - dumps.erase("i") - // output: { "d" : 0.500000, "map" : { "key" : 5 }, "vec" : [ 1, 2, 3 ] } - std::cout << dumps << std::endl; - - // MEO_OPT 表示该变量是一个可选项 - // 即使输入中不存在该字段依然可以读取 - MyStruct b(dumps); - - // output: { "d" : 0.500000, "i" : 0, "map" : { "key" : 5 }, "vec" : [ 1, 2, 3 ] } - // 我们从 dumps 中删除了 "i", 所以 "i" 是 0 - std::cout << json::value(b) << std::endl; + 名字: "MistEO", /* 键的引号可以省略 */ + 😊: '😄', // 表情符可以用作键 + thanks: 'ありがとう', /* 单引号也可以用作字符串 */ + \u006Bey: ['value',], // 正常字符和转义可以混合使用 + inf: +Infinity, nan: NaN, // 数字可以以 '+' 开头 + fractional: .3, integer: 42., // 允许以小数点开头或结尾 + byte_max: 0xff, // 支持十六进制数 + light_speed: +3e8, // 以及科学计数法 +})"; + +auto ret = json::parse5(content5); +if (!ret) { + std::cerr << "解析失败" << std::endl; + return; } +json::value& value = *ret; + +// Output: MistEO +std::cout << value["名字"] << std::endl; +// str = "value" +std::string str = (std::string)value["key"][0]; ``` diff --git a/README_en.md b/README_en.md index 18f9e3f..1bf5953 100644 --- a/README_en.md +++ b/README_en.md @@ -2,9 +2,10 @@ # meojson -现代化的全平台 Json/Json5 解析/生成器,Header-only,并附带大量语法糖! +现代化的全平台 Json/Json5 解析/生成器,Header-only,并且使用了魔法! + +A modern all-platform Json/Json5 parser/serializer, which is header-only and used magic! -A modern all-platform Json/Json5 parser/serializer, which is header-only and contains lots of syntactic sugar! @@ -14,32 +15,201 @@ A modern all-platform Json/Json5 parser/serializer, which is header-only and con - Include the header file in your project, and enjoy it! -```cpp +```c++ #include "json.hpp" ``` - If you want to parse JSON5, please include `json5.hpp` -```cpp +```c++ #include "json5.hpp" ``` -- meojson only depends on STL, but requires c++17 standard +- **meojson** only depends on STL, but requires c++17 standard +- For MSVC, please add `/Zc:preprocessor` to your project +- For AppleClang, please add `-Wno-gnu-zero-variadic-macro-arguments` to your project -## Sample +### Serializing -### Parsing +Here are some basic features: -```cpp -/*** - * from sample/sample.cpp -***/ -#include -#include "json.hpp" +```c++ +json::value j; +j["pi"] = 3.14; +j["happy"] = true; +j["answer"]["everything"] = 42; +j["object"] = { {"currency", "USD"}, {"value", 42.99} }; +``` + +And some interesting features: + +```c++ +std::set set { 1, 2, 3 }; +j["set"] = set; + +// what a crazy type! +std::unordered_map>>> map { + { "key_1", { { { "inner_key_1", { 7, 8, 9 } } }, { { "inner_key_2", { 10 } } } } }, +}; +j["map"] = map; + +// output: +// {"answer":{"everything":42},"happy":true,"map":{"key_1":[{"inner_key_1":[7,8,9]},{"inner_key_2":[10]}]},"object":{"currency":"USD","value":42.990000},"pi":3.140000,"set":[1,2,3]} +std::cout << j << std::endl; +``` + +Then, don’t blink, we changed it back! -void parsing() +```c++ +double pi = (double)j["pi"]; +int answer = (int)j["answer"]["everything"]; + +std::set new_set = (std::set)j["set"]; +// this crazy type again +auto new_map = (std::unordered_map>>>)j["map"]; +``` + +However, for runtime json, we'd better check whether it can be converted first. + +```c++ +if (j["happy"].is>()) { + std::vector vec = (std::vector)j["happy"]; +} +else { + std::cout << "`Oh my god, j[\"happy\"] is not an array." << std::endl; + std::cout << "`Fortunately, I checked it, otherwise it will crash!" << std::endl; +} +``` + +I guess you have understood, yes, **meojson** is not only a json library, but also a serialization library! + +```c++ +struct MyStruct { - std::string content = R"( + int x = 0; + std::vector vec; + // how come it's always you! + std::unordered_map>>> map; + + // then we add a little magic + MEO_JSONIZATION(x, vec, map); +}; + +MyStruct mine; +mine.vec.emplace_back(0.5); +mine.map = { { "key_1", { { { "inner_key_1", { 7, 8, 9 } } }, { { "inner_key_2", { 10 } } } } } }; + +// yes, it’s that intuitive and smooth! +json::value j_mine = mine; +// output: {"map":{"key_1":[{"inner_key_1":[7,8,9]},{"inner_key_2":[10]}]},"vec":[0.500000],"x":0} +std::cout << j_mine << std::endl; + +// exactly, we can also change it back! +MyStruct new_mine = (MyStruct)j_mine; +``` + +Nested calls are also a no-brainer! + +```c++ +struct Outter +{ + int outter_a = 10; + std::vector my_vec; + + MEO_JSONIZATION(outter_a, my_vec); +}; + +Outter o; +o.my_vec.emplace_back(mine); +json::value j_o = o; +// output: {"my_vec":[{"map":{"key_1":[{"inner_key_1":[7,8,9]},{"inner_key_2":[10]}]},"vec":[0.500000],"x":0}],"outter_a":10} +std::cout << j_o << std::endl; + +// same deserialization +Outter new_o = (Outter)j_o; +``` + +For optional fields, we can add `MEO_OPT` to it, so that when converting, if this fields does not exist in json, it will be skipped. + +```c++ +struct OptionalFields +{ + int a = 0; + double b = 0; + std::vector c; + + MEO_JSONIZATION(a, MEO_OPT b, MEO_OPT c); +}; + +json::value ja = { + { "a", 100 }, +}; +if (ja.is()) { + OptionalFields var = (OptionalFields)ja; + // output: 100 + std::cout << var.a << std::endl; +} +``` + +For third-party unhackable types, you need to implement `to_json`, `check_json`, `from_json` + +```c++ +struct ThirdPartyStruct +{ + int a = 100; +}; + +json::value to_json(const ThirdPartyStruct& t) { return t.a; } +bool check_json(const json::value& j, const ThirdPartyStruct&) { return j.is_number(); } +bool from_json(const json::value& j, ThirdPartyStruct& out) { out.a = j.as_integer(); return true; } + +// then you can use it as json +ThirdPartyStruct third; +json::value jthird = third; +ThirdPartyStruct new_third = (ThirdPartyStruct)jthird; + +// or add to your sturcture +struct Outter2 +{ + int outter_a = 10; + ThirdPartyStruct third; + + MEO_JSONIZATION(outter_a, my_vec, third); +}; +``` + +And some trivial features: + +```c++ +// add elements to an array or object via `emplace` +j["set"].emplace(10); +j["object"].emplace("key3", "value3"); + +// merge two arrays +j["set"] += json::array { 11, 12 }; + +// merge two objects +j["object"] |= { + { "key4", 4 }, + { "key5", false }, +}; + +// to string +std::string oneline = j.dumps(); +std::string format = j.dumps(4); + +// save to file +std::ofstream ofs("meo.json"); +ofs << j; +ofs.close(); +``` + +### Parsing + +Now let’s talk about parsing + +```c++ +std::string content = R"( { "repo": "meojson", "author": { @@ -55,110 +225,99 @@ void parsing() { "C_str": "you found me!" } ] } +})"; + +// it's a std::optional +auto ret = json::parse(content); + +if (!ret) { + std::cerr << "Parsing failed" << std::endl; + return; } - )"; +json::value& value = *ret; - auto ret = json::parse(content); +// Output: meojson +std::cout << (std::string)value["repo"] << std::endl; - if (!ret) { - std::cerr << "Parsing failed" << std::endl; - return; - } - json::value& value = ret.value(); // you can use rvalues if needed, like - // `auto value = std::move(ret).value();` - // Output: meojson - std::cout << value["repo"].as_string() << std::endl; - - /* Output: - ChingCdesu's homepage: https://github.com/ChingCdesu - MistEO's homepage: https://github.com/MistEO - */ - for (auto&& [name, homepage] : value["author"].as_object()) { - std::cout << name << "'s homepage: " << homepage.as_string() << std::endl; - } +/* Output: + ChingCdesu's homepage: https://github.com/ChingCdesu + MistEO's homepage: https://github.com/MistEO +*/ +for (auto&& [name, homepage] : (json::object)value["author"]) { + std::cout << name << "'s homepage: " << (std::string)homepage << std::endl; +} +// num = 3.141600 +double num = (double)value["num"]; - // Output: abc - std::string str = (std::string)value["str"]; // it is equivalent to `value["str"].as_string()` - std::cout << str << std::endl; - - // Output: 3.141600 - double num = value["num"].as_double(); // similarly, you can use `(double)value["num"]` - std::cout << num << std::endl; - - // Output: default_value - std::string get = value.get("maybe_exists", "default_value"); - std::cout << get << std::endl; - - // Output: you found me! - std::string nested_get = value.get("A_obj", "B_arr", 1, "C_str", "default_value"); - std::cout << nested_get << std::endl; - - // Output: 1, 2, 3 - // If the "list" is not an array or not exists, it will be a invalid optional; - auto opt = value.find("list"); - if (opt) { - auto& arr = opt.value(); - for (auto&& elem : arr) { - std::cout << elem.as_integer() << std::endl; - } - } - // more examples, it will output 3.141600 - auto opt_n = value.find("num"); - if (opt_n) { - std::cout << opt_n.value() << std::endl; - } - // If you use the `find` without template argument, it will return a `std::optional` - auto opt_v = value.find("not_exists"); - std::cout << "Did we find the \"not_exists\"? " << opt_v.has_value() << std::endl; +// get_value = "default_value" +std::string get_value = value.get("maybe_exists", "default_value"); +std::cout << get_value << std::endl; +``` - bool is_vec = value["list"].is>(); +Like most parsing libraries, this is boring and you don't want to look at this. +So let me show you something interesting. - std::vector to_vec = value["list"].as_collection(); - to_vec = (std::vector)value["list"]; // same as above - to_vec = value["list"].as>(); // same as above +```c++ +// what a magical `get`, you can continuously enter keys or pos! +// nested_get = you found me! +std::string nested_get = value.get("A_obj", "B_arr", 1, "C_str", "default_value"); - // Output: 1, 2, 3 - for (auto&& i : to_vec) { - std::cout << i << std::endl; - } - std::list to_list = value["list"].as_collection(); - to_list = (std::list)value["list"]; // same as above - to_list = value["list"].as>(); // same as above - - std::set to_set = value["list"].as_collection(); - to_set = (std::set)value["list"]; // same as above - to_set = value["list"].as>(); // same as above - - bool is_map = value["author"].is>(); - - std::map to_map = value["author"].as_map(); - to_map = (std::map)value["author"]; // same as above - to_map = value["author"].as>(); // same as above - - auto to_hashmap = value["author"].as_map(); - to_hashmap = (std::unordered_map)value["author"]; // same as above - to_hashmap = value["author"].as>();// same as above - - // Output: "literals" - using namespace json::literals; - auto val = "{\"hi\":\"literals\"}"_json; - std::cout << val["hi"] << std::endl; +// `find` can help you find and check whether the type is correct +// if there is no `num`, the opt_n will be std::nullopt +auto opt_n = value.find("num"); +if (opt_n) { + // output: 3.141600 + std::cout << *opt_n << std::endl; } ``` -### Parsing Json5 +There are also a few tricks you've already seen with Serializing -```cpp -/*** - * from sample/json5_parse.cpp -***/ -#include -#include "json5.hpp" +```c++ +bool is_vec = value["list"].is>(); -void parsing() -{ - std::string_view content = R"( +std::vector to_vec = value["list"].as_collection(); +to_vec = (std::vector)value["list"]; // same as above +to_vec = value["list"].as>(); // same as above + +// Output: 1, 2, 3 +for (auto&& i : to_vec) { + std::cout << i << std::endl; +} + +std::list to_list = value["list"].as_collection(); +to_list = (std::list)value["list"]; // same as above +to_list = value["list"].as>(); // same as above + +std::set to_set = value["list"].as_collection(); +to_set = (std::set)value["list"]; // same as above +to_set = value["list"].as>(); // same as above + +bool is_map = value["author"].is>(); + +std::map to_map = value["author"].as_map(); +to_map = (std::map)value["author"]; // same as above +to_map = value["author"].as>(); // same as above + +auto to_hashmap = value["author"].as_map(); +to_hashmap = (std::unordered_map)value["author"]; // same as above +to_hashmap = value["author"].as>();// same as above +``` + +And... some useless literal syntax + +```c++ +// Output: "literals" +using namespace json::literals; +auto val = "{\"hi\":\"literals\"}"_json; +std::cout << val["hi"] << std::endl; +``` + +But the good news is that we can also parse json5! + +```c++ +std::string_view content5 = R"( // It's a Json5 content { 名字: "MistEO", /* Key's quotes can be omitted */ @@ -171,132 +330,15 @@ void parsing() light_speed: +3e8, // and scientific notation } )"; - auto ret = json::parse5(content); - if (!ret) { - std::cerr << "Parsing failed" << std::endl; - return; - } - json::value& value = ret.value(); // you can use rvalues if needed, like - // `auto value = std::move(ret).value();` - - // Output: MistEO - std::cout << value["名字"] << std::endl; - // Output: value - std::string str = (std::string)value["key"][0]; - std::cout << str << std::endl; - - // for more json::value usage, please refer to sample.cpp +auto ret = json::parse5(content5); +if (!ret) { + std::cerr << "Parsing failed" << std::endl; + return; } -``` +json::value& value = *ret; -### Serializing - -```cpp -/*** - * from sample/sample.cpp -***/ -#include -#include "json.hpp" - -void serializing() -{ - json::value root; - - root["hello"] = "meojson"; - root["Pi"] = 3.1416; - - root["obj"] = { - { "obj_key1", "Hi" }, - { "obj_key2", 123 }, - { "obj_key3", true }, - }; - root["obj"].emplace("obj_key4", 789); - - root["obj"].emplace("obj_key5", json::object { { "key4 child", "i am object value" } }); - root["another_obj"]["child"]["grand"] = "i am grand"; - - // take union - root["obj"] |= json::object { - { "obj_key6", "i am string" }, - { "obj_key7", json::array { "i", "am", "array" } }, - }; - - root["arr"] = json::array { 1, 2, 3 }; - root["arr"].emplace(4); - root["arr"].emplace(5); - root["arr"] += json::array { 6, 7 }; - - std::vector vec = { 1, 2, 3, 4, 5 }; - root["arr from vec"] = vec; - - std::set set = { "a", "bb\n\nb", "cc\t" }; - root["arr from set"] = set; - - std::map map { - { "key1", 1 }, - { "key2", 2 }, - }; - root["obj from map"] = map; - - std::vector>> complex { { { 1, 2, 3 }, { 4, 5 } }, { { 6 }, { 7, 8 } } }; - root["complex"] = json::serialize(complex); - - std::map>> more_complex { - { "key1", { { 1, { 0.1, 0.2 } }, { 2, { 0.2, 0.3 } } } }, - { "key2", { { 3, { 0.4 } }, { 4, { 0.5, 0.6, 0.7 } } } }, - }; - // the "std::map" cannot be converted to json because the key is "int", - // you can set the template parameter "loose" of "serialize" to true, which will make a more relaxed conversion. - root["more_complex"] = json::serialize(more_complex); - - std::cout << root << std::endl; - - std::ofstream ofs("meo.json"); - ofs << root; - ofs.close(); -} -``` - -### JSONization - -```c++ -// if you are using MSVC, please add "/Zc:preprocessor" to your project -// if you are using AppleClang, please add "-Wno-gnu-zero-variadic-macro-arguments" to your project -void test_jsonization() -{ - struct MyStruct - { - std::vector vec; - std::map map; - int i = 0; - double d = 0; - - // MEO_OPT means the var is optional - // and can still be read even if the field doesn't exist in the input. - MEO_JSONIZATION(vec, map, MEO_OPT i, d); - }; - - MyStruct a; - a.vec = { 1, 2, 3 }; - a.map = { { "key", 5 } }; - a.i = 100; - a.d = 0.5; - - json::value dumps = a; - - // output: { "d" : 0.500000, "i" : 100, "map" : { "key" : 5 }, "vec" : [ 1, 2, 3 ] } - std::cout << dumps << std::endl; - - dumps.erase("i") - // output: { "d" : 0.500000, "map" : { "key" : 5 }, "vec" : [ 1, 2, 3 ] } - std::cout << dumps << std::endl; - - // MEO_OPT means the var is optional - // and can still be read even if the field doesn't exist in the input. - MyStruct b(dumps); - - // output: { "d" : 0.500000, "i" : 0, "map" : { "key" : 5 }, "vec" : [ 1, 2, 3 ] } - // "i" is 0 because we erase "i" from the dumps - std::cout << json::value(b) << std::endl; -} +// Output: MistEO +std::cout << value["名字"] << std::endl; +// str = "value" +std::string str = (std::string)value["key"][0]; ``` diff --git a/sample/sample.cpp b/sample/sample.cpp index 0e73600..776fd5e 100644 --- a/sample/sample.cpp +++ b/sample/sample.cpp @@ -1,3 +1,4 @@ +#include #include #include #include @@ -8,358 +9,288 @@ #include "json.hpp" -bool parsing(); -bool parsing_width(); -bool serializing(); -void test_jsonization(); +void serializing(); +void third_party_jsonization(); +void parsing(); int main() { - // std::cout << "\n****** Parsing ******\n" << std::endl; - - // if (!parsing()) { - // return -1; - // } - - // std::cout << "\n****** Serializing ******\n" << std::endl; - - // if (!serializing()) { - // return -1; - // } - - test_jsonization(); + serializing(); + parsing(); return 0; } -bool parsing() -{ - std::string content = R"( +void serializing() { - "repo": "meojson", - "author": { - "MistEO": "https://github.com/MistEO", - "ChingCdesu": "https://github.com/ChingCdesu" - }, - "list": [ 1, 2, 3 ], - "str": "abc\n123", - "num": 3.1416, - "A_obj": { - "B_arr": [ - { "C_str": "i am a distraction" }, - { "C_str": "you found me!" } - ] - } -} - )"; - - auto ret = json::parse(content); - - if (!ret) { - std::cerr << "Parsing failed" << std::endl; - return false; - } - json::value& value = ret.value(); // you can use rvalues if needed, like - // `auto value = std::move(ret).value();` - // Output: meojson - std::cout << value["repo"].as_string() << std::endl; - - /* Output: - ChingCdesu's homepage: https://github.com/ChingCdesu - MistEO's homepage: https://github.com/MistEO - */ - for (auto&& [name, homepage] : value["author"].as_object()) { - std::cout << name << "'s homepage: " << homepage.as_string() << std::endl; - } - - // Output: abc - std::string str = (std::string)value["str"]; // it is equivalent to `value["str"].as_string()` - std::cout << str << std::endl; + /* Here are some basic features: */ - // Output: 3.141600 - double num = value["num"].as_double(); // similarly, you can use `(double)value["num"]` - std::cout << num << std::endl; + json::value j; + j["pi"] = 3.14; + j["happy"] = true; + j["answer"]["everything"] = 42; + j["object"] = { { "currency", "USD" }, { "value", 42.99 } }; - // Output: default_value - std::string get = value.get("maybe_exists", "default_value"); - std::cout << get << std::endl; + /* And some interesting features: */ - // Output: you found me! - std::string nested_get = value.get("A_obj", "B_arr", 1, "C_str", "default_value"); - std::cout << nested_get << std::endl; - - // Output: 1, 2, 3 - // If the "list" is not an array or not exists, it will be a invalid optional; - auto opt = value.find("list"); - if (opt) { - auto& arr = opt.value(); - for (auto&& elem : arr) { - std::cout << elem.as_integer() << std::endl; - } - } - // more examples, it will output 3.141600 - auto opt_n = value.find("num"); - if (opt_n) { - std::cout << opt_n.value() << std::endl; - } - // If you use the `find` without template argument, it will return a `std::optional` - auto opt_v = value.find("not_exists"); - std::cout << "Did we find the \"not_exists\"? " << opt_v.has_value() << std::endl; + std::set set { 1, 2, 3 }; + j["set"] = set; - bool is_vec = value["list"].is>(); - if (is_vec) { - std::vector to_vec = value["list"].as_collection(); - to_vec = (std::vector)value["list"]; - to_vec = value["list"].as>(); - - // Output: 1, 2, 3 - for (auto&& i : to_vec) { - std::cout << i << std::endl; - } - } + // what a crazy type! + std::unordered_map>>> map { + { "key_1", { { { "inner_key_1", { 7, 8, 9 } } }, { { "inner_key_2", { 10 } } } } }, + }; + j["map"] = map; - std::list to_list = value["list"].as_collection(); - to_list = (std::list)value["list"]; - to_list = value["list"].as>(); + // output: + // {"answer":{"everything":42},"happy":true,"map":{"key_1":[{"inner_key_1":[7,8,9]},{"inner_key_2":[10]}]},"object":{"currency":"USD","value":42.990000},"pi":3.140000,"set":[1,2,3]} + std::cout << j << std::endl; - std::set to_set = value["list"].as_collection(); - to_set = (std::set)value["list"]; - to_set = value["list"].as>(); + /* Then, don’t blink, we changed it back! */ - bool is_map = value["author"].is>(); - if (is_map) { - std::map to_map = value["author"].as_map(); - to_map = (std::map)value["author"]; - to_map = value["author"].as>(); - } + double pi = (double)j["pi"]; + int answer = (int)j["answer"]["everything"]; - auto to_hashmap = value["author"].as_map(); - to_hashmap = (std::unordered_map)value["author"]; - to_hashmap = value["author"].as>(); + std::set new_set = (std::set)j["set"]; + // this crazy type again + auto new_map = (std::unordered_map>>>)j["map"]; - using namespace json::literals; - json::value val = "{\"hi\":\"literals\"}"_json; - std::cout << val["hi"] << std::endl; + /* However, for runtime json, we'd better check whether it can be converted first. */ - std::vector vec_j = { '{', '"', 'k', '"', ':', '"', 'v', '"', '}' }; - auto vecj_opt = json::parse(vec_j); - if (vecj_opt) { - std::ignore = vecj_opt->dumps(); + if (j["happy"].is>()) { + std::vector vec = (std::vector)j["happy"]; } - - return true; -} - -bool parsing_width() -{ - std::wstring_view content = LR"( -{ - "repo": "meojson", - "author": { - "MistEO": "https://github.com/MistEO", - "ChingCdesu": "https://github.com/ChingCdesu" - }, - "list": [ 1, 2, 3 ], - "str": "abc\n123", - "num": 3.1416, - "A_obj": { - "B_arr": [ - { "C_str": "i am a distraction" }, - { "C_str": "you found me!" } - ] + else { + std::cout << "`Oh my god, j[\"happy\"] is not an array." << std::endl; + std::cout << "`Fortunately, I checked it, otherwise it will crash!" << std::endl; } -} - )"; - auto ret = json::parse(content); + /* I guess you have understood, yes, **meojson** is not only a json library, but also a serialization library! */ - if (!ret) { - std::cerr << "Parsing failed" << std::endl; - return false; - } - json::wvalue& value = ret.value(); // you can use rvalues if needed, like - // `auto value = std::move(ret).value();` - // Output: meojson - std::wcout << value[L"repo"].as_string() << std::endl; + struct MyStruct + { + int x = 0; + std::vector vec; + // how come it's always you! + std::unordered_map>>> map; - /* Output: - ChingCdesu's homepage: https://github.com/ChingCdesu - MistEO's homepage: https://github.com/MistEO - */ - for (auto&& [name, homepage] : value[L"author"].as_object()) { - std::wcout << name << "'s homepage: " << homepage.as_string() << std::endl; - } + // then we add a little magic + MEO_JSONIZATION(x, vec, map); + }; - // Output: abc - std::wstring str = (std::wstring)value[L"str"]; // it is equivalent to `value["str"].as_string()` - std::wcout << str << std::endl; + MyStruct mine; + mine.vec.emplace_back(0.5); + mine.map = { { "key_1", { { { "inner_key_1", { 7, 8, 9 } } }, { { "inner_key_2", { 10 } } } } } }; - // Output: 3.141600 - double num = value[L"num"].as_double(); // similarly, you can use `(double)value["num"]` - std::wcout << num << std::endl; + // yes, it’s that intuitive and smooth! + json::value j_mine = mine; + // output: {"map":{"key_1":[{"inner_key_1":[7,8,9]},{"inner_key_2":[10]}]},"vec":[0.500000],"x":0} + std::cout << j_mine << std::endl; - // Output: default_value - std::wstring get = value.get(L"maybe_exists", L"default_value"); - std::wcout << get << std::endl; + // exactly, we can also change it back! + MyStruct new_mine = (MyStruct)j_mine; - // Output: you found me! - std::wstring nested_get = value.get(L"A_obj", L"B_arr", 1, L"C_str", L"default_value"); - std::wcout << nested_get << std::endl; + /* Nested calls are also a no-brainer! */ - // Output: 1, 2, 3 - // If the "list" is not an array or not exists, it will be a invalid optional; - auto opt = value.find(L"list"); - if (opt) { - auto& arr = opt.value(); - for (auto&& elem : arr) { - std::cout << elem.as_integer() << std::endl; - } - } - // more examples, it will output 3.141600 - auto opt_n = value.find(L"num"); - if (opt_n) { - std::cout << opt_n.value() << std::endl; - } - // If you use the `find` without template argument, it will return a `std::optional` - auto opt_v = value.find(L"not_exists"); - std::cout << "Did we find the \"not_exists\"? " << opt_v.has_value() << std::endl; + struct Outter + { + int outter_a = 10; + std::vector my_vec; - std::wcout << value.format(2) << std::endl; + MEO_JSONIZATION(outter_a, my_vec); + }; - using namespace json::literals; - auto val = LR"({"hi":"literals"})"_json; - std::wcout << val[L"hi"] << std::endl; + Outter o; + o.my_vec.emplace_back(mine); + json::value j_o = o; + // output: + // {"my_vec":[{"map":{"key_1":[{"inner_key_1":[7,8,9]},{"inner_key_2":[10]}]},"vec":[0.500000],"x":0}],"outter_a":10} + std::cout << j_o.to_string() << std::endl; - return true; -} + // same deserialization + Outter new_o = (Outter)j_o; -bool serializing() -{ - json::value root; + /* For optional fields, we can add `MEO_OPT` to it, so that when converting, if this fields does not exist in json, it will be skipped. */ - root["hello"] = "meojson"; - root["Pi"] = 3.1416; + struct OptionalFields + { + int a = 0; + double b = 0; + std::vector c; - root["obj"] = { - { "obj_key1", "Hi" }, - { "obj_key2", 123 }, - { "obj_key3", true }, + MEO_JSONIZATION(a, MEO_OPT b, MEO_OPT c); }; - root["obj"].emplace("obj_key4", 789); - - root["obj"].emplace("obj_key5", json::object { { "key4 child", "i am object value" } }); - root["another_obj"]["child"]["grand"] = "i am grand"; - // take union - root["obj"] |= json::object { - { "obj_key6", "i am string" }, - { "obj_key7", json::array { "i", "am", "array" } }, + json::value ja = { + { "a", 100 }, }; + if (ja.is()) { + OptionalFields var = (OptionalFields)ja; + std::cout << var.a << std::endl; + } - root["arr"] = json::array { 1, 2, 3 }; - root["arr"].emplace(4); - root["arr"].emplace(5); - root["arr"] += json::array { 6, 7 }; - - std::vector vec = { 1, 2, 3, 4, 5 }; - root["arr from vec"] = vec; + /* For third-party unhackable types, you need to implement `to_json`, `check_json`, `from_json` */ + third_party_jsonization(); - std::set set = { "a", "bb\n\nb", "cc\t" }; - root["arr from set"] = set; + /* And some trivial features: */ - std::map map { - { "key1", 1 }, - { "key2", 2 }, - }; - root["obj from map"] = map; + // add elements to an array or object via `emplace` + j["set"].emplace(10); + j["object"].emplace("key3", "value3"); - std::vector>> complex { { { 1, 2, 3 }, { 4, 5 } }, { { 6 }, { 7, 8 } } }; - root["complex"] = json::serialize(complex); + // merge two arrays + j["set"] += json::array { 11, 12 }; - std::map, std::map>> more_complex { - { { "i", "am", "key1" }, { { 1, { 0.1, 0.2 } }, { 2, { 0.2, 0.3 } } } }, - { { "key2" }, { { 3, { 0.4 } }, { 4, { 0.5, 0.6, 0.7 } } } }, + // merge two objects + j["object"] |= { + { "key4", 4 }, + { "key5", false }, }; - // the "std::map" cannot be converted to json because the key is "std::vector", - // you can set the template parameter "loose" of "serialize" to true, which will make a more relaxed conversion. - root["more_complex"] = json::serialize(more_complex); - // for test - root["a\\n"] = "1a\\n"; - root["a\n"] = "2a\n"; - - std::cout << root << std::endl; + // to string + std::string oneline = j.dumps(); + std::string format = j.dumps(4); + // save to file std::ofstream ofs("meo.json"); - ofs << root; + ofs << j; ofs.close(); - - return true; } -struct TypeFromOtherLibrary +struct ThirdPartyStruct { - int a_i = 100; + int a = 100; }; -json::value to_json(const TypeFromOtherLibrary& t) +json::value to_json(const ThirdPartyStruct& t) { - return t.a_i; + return t.a; } -bool check_json(const json::value& j, const TypeFromOtherLibrary&) +bool check_json(const json::value& j, const ThirdPartyStruct&) { return j.is_number(); } -bool from_json(const json::value& j, TypeFromOtherLibrary& out) +bool from_json(const json::value& j, ThirdPartyStruct& out) { - out.a_i = j.as_integer(); + out.a = j.as_integer(); return true; } -void test_jsonization() +void third_party_jsonization() { - struct AAA - { - int a_i = 100; + // then you can use it as json + ThirdPartyStruct third; + json::value jthird = third; + ThirdPartyStruct new_third = (ThirdPartyStruct)jthird; - TypeFromOtherLibrary other; + // or add to your sturcture + struct Outter2 + { + int outter2_a = 10; + ThirdPartyStruct third; - MEO_JSONIZATION(a_i, other); + MEO_JSONIZATION(outter2_a, third); }; +} - struct BBB - { - int b_i = 10; - double b_d = 0.5; +void parsing() +{ + /* Now let’s talk about parsing */ - std::vector b_aaa; + std::string content = R"( +{ + "repo": "meojson", + "author": { + "MistEO": "https://github.com/MistEO", + "ChingCdesu": "https://github.com/ChingCdesu" + }, + "list": [ 1, 2, 3 ], + "str": "abc\n123", + "num": 3.1416, + "A_obj": { + "B_arr": [ + { "C_str": "i am a distraction" }, + { "C_str": "you found me!" } + ] + } +})"; - MEO_JSONIZATION(b_i, MEO_OPT b_d, b_aaa); - }; + // it's a std::optional + auto ret = json::parse(content); - std::vector my_vec(3); - json::value j = my_vec; - std::cout << j << std::endl; + if (!ret) { + std::cerr << "Parsing failed" << std::endl; + return; + } + json::value& value = *ret; - std::vector result(j); - std::cout << json::value(result) << std::endl; + // Output: meojson + std::cout << (std::string)value["repo"] << std::endl; - // MyStruct a; + /* Output: + ChingCdesu's homepage: https://github.com/ChingCdesu + MistEO's homepage: https://github.com/MistEO + */ + for (auto&& [name, homepage] : (json::object)value["author"]) { + std::cout << name << "'s homepage: " << (std::string)homepage << std::endl; + } + // num = 3.141600 + double num = (double)value["num"]; - // a.vec = { 1, 2, 3 }; - // a.map = { { "key", 5 } }; - // a.i = 100; - // a.d = 0.5; + // get_value = "default_value" + std::string get_value = value.get("maybe_exists", "default_value"); + std::cout << get_value << std::endl; - // json::object j = a.to_json(); - // std::cout << j << std::endl; + /* Like most parsing libraries, this is boring and you don't want to look at this. + So let me show you something interesting. */ - //// for test MEO_OPT - // j.erase("i"); + // what a magical `get`, you can continuously enter keys or pos! + // nested_get = you found me! + std::string nested_get = value.get("A_obj", "B_arr", 1, "C_str", "default_value"); - // MyStruct b; - // bool loaded = b.from_json(j); + // `find` can help you find and check whether the type is correct + // if there is no `num`, the opt_n will be std::nullopt + auto opt_n = value.find("num"); + if (opt_n) { + // output: 3.141600 + std::cout << *opt_n << std::endl; + } - // std::cout << "loaded: " << loaded << std::endl; - // std::cout << b.myCustomStructure.b_i << std::endl; + /* There are also a few tricks you've already seen with Serializing */ + + bool is_vec = value["list"].is>(); + + std::vector to_vec = value["list"].as_collection(); + to_vec = (std::vector)value["list"]; // same as above + to_vec = value["list"].as>(); // same as above + + // Output: 1, 2, 3 + for (auto&& i : to_vec) { + std::cout << i << std::endl; + } + + std::list to_list = value["list"].as_collection(); + to_list = (std::list)value["list"]; // same as above + to_list = value["list"].as>(); // same as above + + std::set to_set = value["list"].as_collection(); + to_set = (std::set)value["list"]; // same as above + to_set = value["list"].as>(); // same as above + + bool is_map = value["author"].is>(); + + std::map to_map = value["author"].as_map(); + to_map = (std::map)value["author"]; // same as above + to_map = value["author"].as>(); // same as above + + auto to_hashmap = value["author"].as_map(); + to_hashmap = (std::unordered_map)value["author"]; // same as above + to_hashmap = value["author"].as>(); // same as above + + /* And... some useless literal syntax */ + + // Output: "literals" + using namespace json::literals; + auto val = "{\"hi\":\"literals\"}"_json; + std::cout << val["hi"] << std::endl; } diff --git a/sample/json5_parse.cpp b/test/json5_test.cpp similarity index 84% rename from sample/json5_parse.cpp rename to test/json5_test.cpp index 2c73765..18e2264 100644 --- a/sample/json5_parse.cpp +++ b/test/json5_test.cpp @@ -1,26 +1,24 @@ #include +#include "json5_test.h" #include "json5.hpp" bool parsing(); bool parsing_error(); -int main() +bool test_json5() { /*** Parsing Json5 ***/ - std::cout << "\n****** Parsing 5 ******\n" << std::endl; if (!parsing()) { - return -1; + return false; } - std::cout << "\n****** Parsing error 5 ******\n" << std::endl; - if (!parsing_error()) { - return -1; + return false; } - return 0; + return true; } bool parsing() @@ -48,16 +46,16 @@ bool parsing() std::cerr << error_message << std::endl; return false; } - { + { auto another_ret = json::parse5(content, &error_message); if (!another_ret) { std::cerr << "Parsing failed" << std::endl; std::cerr << error_message << std::endl; return false; - }//C-style strings can also be used as input. + } // C-style strings can also be used as input. } json::value& value = ret.value(); // you can use rvalues if needed, like - // `auto value = std::move(ret).value();` + // `auto value = std::move(ret).value();` // Output: MistEO std::cout << value["名字"].as_string() << std::endl; @@ -83,8 +81,6 @@ bool parsing_error() std::string error_msg; auto err_ret = json::parse5(error_content, &error_msg); if (!err_ret) { - std::cout << "Parsing failed" << std::endl; - std::cout << error_msg << std::endl; return true; } diff --git a/test/json5_test.h b/test/json5_test.h new file mode 100644 index 0000000..8bd411b --- /dev/null +++ b/test/json5_test.h @@ -0,0 +1,3 @@ +#pragma once + +extern bool test_json5(); diff --git a/test/main.cpp b/test/main.cpp index b8b58f9..374c1d6 100644 --- a/test/main.cpp +++ b/test/main.cpp @@ -2,6 +2,7 @@ #include "include_test.h" #include "serializing_test.h" +#include "json5_test.h" int main() { @@ -16,6 +17,9 @@ int main() std::cout << "\n*** include_test ***\n" << std::endl; success &= include_test(); + std::cout << "\n*** json5_test ***\n" << std::endl; + success &= test_json5(); + if (!success) { std::cout << "\n****** Test failed ******\n" << std::endl; return -1;