nanojson
所属分类:GPT/ChatGPT
开发工具:C++
文件大小:0KB
下载次数:0
上传日期:2023-05-07 08:40:42
上 传 者:
sh-1993
说明: 单C++头专用JSON读取器编写器
(Single C++ header-only JSON reader writer)
文件列表:
nanojson-3/ (0, 2023-05-07)
nanojson-3/.editorconfig (115, 2023-05-07)
nanojson-3/CMakeLists.txt (429, 2023-05-07)
nanojson-3/LICENSE (1068, 2023-05-07)
nanojson-3/nanojson3.h (102098, 2023-05-07)
nanojson-3/nanojson3.samples.cpp (16794, 2023-05-07)
nanojson-3/nanojson3.vcxitems (875, 2023-05-07)
# nanojson (v3) - Simple JSON reader/writer for C++17
Single C++ header only. ALL in [nanojson3.h](https://github.com/ttsuki/nanojson/blob/master/nanojson3.h).
This project is an experimental, proof-of-concept, implementation of JSON library using modern C++17 functions.
This is the version 3 of the nanojson library.
There is no API compatibility from previous major versions of the nanojson.
The previous versions are in `v1` or `v2` branch.
## License
[MIT License. (c) 2016-2023 ttsuki](https://github.com/ttsuki/nanojson/blob/master/LICENSE)
## The Concept and basic functions
This library provides simply a JSON implementation type `json` and i/o functions:
```cpp
// psudo-code
namespace njs3 {
class json
{
// typedefs
using js_undefined = ...;
using js_null = std::nullptr_t;
using js_boolean = bool;
using js_integer = long long int;
using js_floating = long double;
using js_string = std::string -like container;
using js_array = std::vector -like container;
using js_object = std::map -like container;
// the value holder
private: std::variant value_;
// constructor family
json(js_null);
json(js_boolean);
json(js_object);
template json(T); // and templated ctor imports various types with json_serializer.
// `.is_*` family checks value contains the type
bool is_undefined();
bool is_defined(); // not a undefined.
bool is_null();
bool is_boolean();
bool is_object();
// `.as_*` family accesses value with type checking or returns nullptr
js_null* as_null();
js_boolean* as_boolean();
js_integer* as_integer();
js_object* as_object();
// `.get_*` family accesses value with type checking or throws `bad_access` exception
js_null get_null();
js_boolean get_boolean();
js_integer get_integer();
js_object get_object();
// `operator[]` family accesses children elements.
// returns the reference to the child element of `js_array`/`js_object`.
// if no such element exisit, returns`js_undefined`.
json& operator[int index];
json& operator[string key];
};
enum json_parse_option { ... };
enum json_serialize_option { ... };
struct json_floating_format_options{ ... };
// parser and serializer
json parse_json(string_view sv, json_parse_option loose = json_parse_option::default_option)
string serialize_json(json value, json_serialize_option option = json_serialize_option::none, json_floating_format_options floating_format = {})
// iostream operators and maniplators
// usage: `std::cin >> njs3::json_set_option(njs3::json_parse_option::default) >> json;`
// usage: `std::cout << njs3::json_set_option(njs3::json_serialize_option::pretty) << json;`
// `json` constructor customization point. (for templated constructor `json(T)`)
template
struct json_serializer
{
static json serialize(T) { return /* implement here */; }
};
} // end of namespace
```
## Sample Code Snippets
Here, some snippets may be useful to learn usage of this library.
[nanojson3.samples.cpp](https://github.com/ttsuki/nanojson/blob/master/nanojson3.samples.cpp)
### Simple iostream/string i/o interface.
```cpp
std::cout << njs3::json_out_pretty << njs3::json::parse(R"([123, 456, "abc"])") << "\n";
```
```json
[
123,
456,
"abc"
]
```
```cpp
njs3::json json;
istream >> json; // parse input
std::cout << njs3::json_out_pretty << json; // output pretty
```
### Some loose parse option by flags.
input (parse with some flags)
```js
auto src = R""(
// loose json
{
// in LOOSE MODE, block/line comments are allowed.
"comments": [ "not comment0"
,"not comment1" // line comment // ," still line comment" */ ," still line comment" /*
,"not comment2" /*** block comment ***/ ,"not comment3"
/*//*//** */ ,"not comment4" /* block comment
// still in block comment **/ ,"not comment5" // line comment */ still line comment
/*/, "comment"
/*/, "not comment6"
/*/, "block comment"
/*/, "not comment7"
//*/, "line comment"
,"not comment8"
],
naked_key: "hello world" // in LOOSE MODE, non-quoted keys are allowed.
, // in LOOSE MODE, trailing comma is allowed.
}
)"";
```
```cpp
std::cout << njs3::json_out_pretty << parse_json(
src
, njs3::json_parse_option::default_option // default allows utf-8 bom, unescaped forward slash '/'
| njs3::json_parse_option::allow_comment // allows block/line comments
| njs3::json_parse_option::allow_trailing_comma // allows comma following last element
| njs3::json_parse_option::allow_unquoted_object_key // allows naked object key
// or simply ` njs3::json_parse_option::all` enables all loose option flags.
);
```
output `.json` is
```json
{
"comments": [
"not comment0",
"not comment1",
"not comment2",
"not comment3",
"not comment4",
"not comment5",
"not comment6",
"not comment7",
"not comment8"
],
"naked_key": "hello world"
}
```
### Basic Read/Write Access To JSON Object
input
````cpp
njs3::json json = njs3::parse_json(R""(
````
```js
{
"null_literal" : null,
"bool_true" : true,
"bool_false" : false,
"integer" : 1234567890123456789, // parsed to js_integer 1234567890123456789
"float1" : 123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890, // parsed to js_floating 1.234568e+119
"float2" : 1.234567e+89, // parsed to js_floating 1.234567e+89,
"strings" : {
"a": "a",
"にほんご": "\/\/あいう\n\tえお",
"": "\u26a1", // \u???? characters will be decoded to utf-8 sequence.
"": "\uD83D\uDE03", // surrogate pairs
"aa亜": "\u0061\u0061\u030A\u0928\u093F\u4E9C\uD800\uDC83"
},
"test_array": [1, 2, 3, "a", "b", "c"]
}
```
````cpp
)"", njs3::json_parse_option::allow_comment);
std::cout << njs3::json_out_pretty << json;
````
The Output `.json` is
```json
{
"bool_false": false,
"bool_true": true,
"float1": 1.23456789e+119,
"float2": 1.234567e+89,
"integer": 1234567890123456789,
"null_literal": null,
"strings": {
"a": "a",
"aa亜 ": "aa亜 ",
"": "",
"にほんご": "\/\/あいう\n\tえお",
"": ""
},
"test_array": [
1,
2,
3,
"a",
"b",
"c"
]
}
```
※ Note: Numbers are parsed into integer or floating-point types,
the type is determined by their value range and representation.
And that `json` object can be accessed by `operator[]`.
```cpp
//.cpp
#define DEBUG_OUTPUT(...) (#__VA_ARGS__) << " => " << (__VA_ARGS__) << "\n"
// Writing access:
json["this"] = "is ok."; // will create new property `"this": "is ok."` into json.
// Writing to undefined reference throws `bad_access` exception.
try
{
json["this"]["node"] = 123; // throws bad_access: invalid reference
}
catch (const njs3::bad_access& x)
{
std::cerr << x.what() << std::endl;
}
// Reading access:
njs3::js_integer integer = json["integer"].get_integer(); // OK
//njs3::js_floating integer = json["integer"].get_floating(); // throws bad_access (type mismatch)
std::cout << DEBUG_OUTPUT(integer);
njs3::js_floating float1 = json["float1"].get_floating(); // OK
//njs3::js_integer float1 = json["float1"].get_integer(); // throws bad_access (type mismatch)
std::cout << DEBUG_OUTPUT(float1);
njs3::js_floating integer_as_number = json["integer"].get_number(); // OK (converted to js_floating)
njs3::js_floating float1_as_number = json["float1"].get_number(); // OK
njs3::js_floating float2_as_number = json["float2"].get_number(); // OK
std::cout << DEBUG_OUTPUT(integer_as_number);
std::cout << DEBUG_OUTPUT(float1_as_number);
std::cout << DEBUG_OUTPUT(float2_as_number);
// .o(`operator []` is used to reference sub-nodes)
std::cout << DEBUG_OUTPUT(json["strings"]["にほんご"].get_string()); // "//あいう\n\tえお"
// std::cout << DEBUG_OUTPUT(json["strings"]["not defined value"].get_string()); // throws bad_access (no such key.)
std::cout << DEBUG_OUTPUT(json["strings"]["not defined value"].get_string_or("failed")); // "failed": get_*_or method doesn't throw exception, returns argument `default_value` instead.
// type-mismatched get access throws bad_access.
try
{
(void)json["this"].get_integer(); // throws bad_access: json["this"] is string.
(void)json["this"]["foobar"].get_null(); // throws bad_access: json["this"]["foobar"] is undefined (not a null).
}
catch (const njs3::bad_access& x)
{
std::cerr << x.what() << std::endl;
}
std::cout << DEBUG_OUTPUT(json["strings"].get_string_or("failed")); // "failed": type mismatch json is not string.
// Testing node existence:
std::cout << DEBUG_OUTPUT(json.is_defined()); // true
std::cout << DEBUG_OUTPUT(json.is_array()); // false
std::cout << DEBUG_OUTPUT(json.is_object()); // true
// Only referencing undefined node is not error.
std::cout << DEBUG_OUTPUT(json["aaaa"].is_defined()); // false: no such key.
std::cout << DEBUG_OUTPUT(json["test_array"][12345].is_defined()); // false: index is out of range.
std::cout << DEBUG_OUTPUT(json["this"].is_defined()); // true
std::cout << DEBUG_OUTPUT(json["this"]["node"].is_defined()); // false: doesn't emit bad_access
std::cout << DEBUG_OUTPUT(json["Non-existent node"]["a child"].is_defined()); // false: doesn't emit bad_access
```
### Making JSON Values From Scratch
```cpp
//.cpp
// Makes array from values
njs3::json json = njs3::js_array{1, 2, 3, "a", true, false, 4.5, nullptr};
if (auto a = json.as_array()) // gets js_array interface (is simply std::vector
{
a->push_back(123);
a->push_back("abc");
}
std::cout << njs3::json_out_pretty << json << std::endl;
```
```cpp
//.cpp
njs3::json json = njs3::js_object{
{"a", 1},
{"b", 2},
{"c", njs3::js_array{"X", "Y", "Z", 1, 2, 3}},
};
if (auto a = json.as_object()) // gets js_object interface (is simply std::map)
{
a->insert_or_assign("d", 12345);
a->insert_or_assign("e", "abc");
a->insert_or_assign("f", njs3::js_object{{"f1", 123}, {"f2", 456}, {"f3", 789},});
}
std::cout << njs3::json_out_pretty << json << std::endl;
```
### Making JSON Values From STL Containers
Let's serialize STL containers into `json`.
```cpp
//.cpp
{
// Makes array of array from STL containers
njs3::json json = std::vector>
{
{1, 2, 3},
{4, 5, 6},
{7, 8, 9}
};
std::cout << njs3::json_out_pretty << json << std::endl;
// `get_*` method assumes type is array. if not, throws bad_access
for (auto&& row : json.get_array())
{
// `get_*_or` method checks type is array. if not, returns default value in argument
for (auto&& column : row.get_array_or({}))
std::cout << " " << column;
std::cout << "\n";
}
}
{
// std::map is converted json::js_object
njs3::json json = std::map{{"a", 1}, {"b", 2}};
std::cout << njs3::json_out_pretty << json << std::endl;
// makes { "a": 1, "b": 2 }
}
```
### Serializing User Defined Types Into `json`
Let's serialize user-defined type into `json`.
```cpp
//.cpp
{
// example User defined type.
struct custom_struct
{
std::string title{};
int value{};
// returns json-formated string (or simply `nanojson3::json`)
[[nodiscard]] std::string to_json() const
{
return njs3::json(njs3::js_object{
{"title", title},
{"value", value},
}).serialize();
}
};
// Converts from user-defined struct by member function such
// - string to_json() const;
// - json to_json() const;
// or non-member functions searched global/ADL such
// - string to_json(s);
// - json to_json(s);
njs3::json test = custom_struct{"the answer", 42};
std::cout << DEBUG_OUTPUT(test);
// Mix use with json_convertible objects.
njs3::json json = std::array{
{
{"the answer", 42},
{"the answer squared", 42 * 42},
}
};
// std::array of json convertible type is converted into json::js_array
auto ref = json.as_array(); // get js_array interface (is simply std::vector)
ref->emplace_back(custom_struct{"the answer is", 43});
ref->emplace_back(custom_struct{"the answer is", 44});
ref->emplace_back(custom_struct{"the answer is", 45});
// makes
// [
// {"title": "the answer", "value": 42},
// {"title": "the answer squared", "value": 1764},
// {"title": "the answer is", "value": 43},
// {"title": "the answer is", "value": 44},
// {"title": "the answer is", "value": 45}
// ]
std::cout << njs3::json_out_pretty << DEBUG_OUTPUT(json);
// tuple is converted into array
njs3::json json2 = std::tuple{42, 42.195, {"hello", 12345}};
std::cout << njs3::json_out_pretty << DEBUG_OUTPUT(json2);
// makes
// [
// 42,
// 42.195,
// {
// "title": "hello"
// "value": 12345,
// }
// ]
}
```
.o( if a user-defined type is in another library and it cannot be changed, what should I do? )
### Adding User-defined JSON Serializer (User-defined JSON Constructor Plug-in system)
The nanojson provides constructor plug-in interface.
Here are unchangeable `Vector3f` and `Matrix3x3f` provided by another library.
```cpp
//.cpp
namespace foobar_library
{
struct Vector3f final
{
float x, y, z;
};
struct Matrix3x3f final
{
Vector3f row0, row1, row2;
};
}
```
You can specialize the `json_serializer` class template to serialize `Vector3f` into `json`.
The prototype of `json_serializer` is
```cpp
// cpp
namespace nanojson3
{
template // T is source type.
struct json_serializer // U is placeholder for specializations. (std::enable_if_t or std::void_t)
{
static json serialize(T value) { return /* implement here! */; }
};
}
```
Making such specialization for type `T` makes `json(T)` constructor callable.
Let `json_serializer` as
```cpp
// cpp
// Vector3f JSON serializer
template <>
struct njs3::json_serializer
{
static json serialize(const foobar_library::Vector3f& val)
{
return njs3::js_object
{
{"x", val.x},
{"y", val.y},
{"z", val.z},
};
}
};
```
Or, just implement `to_json(T)` ADL or global function also makes `json(T)` constructor callable.
(but it may cause conflicts with the original `foobar_library`'s functions).
```cpp
// cpp
namespace foobar_library
{
static njs3::json to_json(const Matrix3x3f& val)
{
// std::array will be converted to `json` via another `json_serializer`.
return std::array{val.row0, val.row1, val.row2};
}
}
```
then we can use it.
```cpp
//.cpp
void fixed_user_defined_types()
{
const foobar_library::Vector3f input = {1.0f, 2.0f, 3.0f};
// Convert Vector3f into json by `json_serializer`
njs3::json json_from_vector3f = input;
std::cout << std::fixed << DEBUG_OUTPUT(json_from_vector3f);
// makes output like {"x":1.0000,"y":2.0000,"z":3.0000}.
// Convert Matrix3x3f into json by `foobar_library::to_json`
njs3::json json_from_matrix3x3f = std::vector{
{
{1.0f, 2.0f, 3.0f},
{4.0f, 5.0f, 6.0f},
{7.0f, 8.0f, 9.0f},
},
{
{100.1f, 200.2f, 300.3f},
{400.4f, 500.5f, 600.6f},
{700.7f, 800.8f, 900.9f},
},
};
// Output float format can be set by i/o manipulators.
std::cout << std::fixed << std::setprecision(3) << njs3::json_out_pretty << DEBUG_OUTPUT(json_from_matrix3x3f);
std::cout << std::scientific << std::setprecision(16) << njs3::json_out_pretty << DEBUG_OUTPUT(json_from_matrix3x3f);
}
```
### Built-in json_serializer plug-ins
Some json_serializer are implemented as built-in:
- built-in `json_serializer` for primitives:
- map all integral types (except `char`) to `js_integer`
- map all floating-point types to `js_floating`
- map `const char*` to `js_string`
- map `container` to `js_string`
- built-in `json_serializer` for STL containers:
- map `container` to `js_array` if `T` is json-convertible.
- map `std::tuple` to `js_array` if all of `U...` is json-convertible.
- map `container<[K,V]>` to `js_object` if `K` is js_object_key-convertible and `V` is json-convertible.
- built-in `json_serializer` for User-defined types:
- map `T` to `json` if `T` has member function `json_string T::to_json() const`
- map `T` to `json` if there is ADL/global function `json_string to_json(T)`
- map `T` to `json` if `T` has member function `json T::to_json() const`
- map `T` to `json` if there is ADL/global function `json to_json(T)`
### EOF
Have fun.
近期下载者:
相关文件:
收藏者: