mysvac-jsonlib is a C++20 JSON library that provides concise and efficient JSON parsing, manipulation, and serialization.
You can find detailed documentation on this repository’s GitHub Pages page.
Install by vcpkg: ( Update vcpkg port first )
vcpkg install mysvac-jsonlib
CMake:
find_package(mysvac-jsonlib CONFIG REQUIRED)
...
target_link_mysvac_jsonlib(main PRIVATE)
Use in your project:
import std; // use std headers or module
import mysvac.json; // Import the mysvac-jsonlib library
using namespace mysvac; // Use the namespace to simplify codeJSON has six basic types, and this library uses an enum to represent them:
enum class Type{
eNul = 0, ///< null type
eBol, ///< boolean type
eNum, ///< number type
eStr, ///< string type
eArr, ///< array type
eObj ///< object type
};This enum is located in the mysvac::json namespace, which also contains an important class template Json — a container that can store any JSON data structure.
template<
bool UseOrderedMap = true,
template<typename U> class VecAllocator = std::allocator,
template<typename U> class MapAllocator = std::allocator,
template<typename U> class StrAllocator = std::allocator
>
class Json;Although you cannot specify the exact implementations of objects (maps), arrays, and strings, you can specify their memory allocators.
UseOrderedMap: Whether to use an ordered map (std::map) or an unordered map (std::unordered_map).VecAllocator: Allocator template used by the array type (std::vector).MapAllocator: Allocator template used by the object (map) type.StrAllocator: Allocator template used by the string type (std::basic_string).
For convenience, a default Json alias is provided in the mysvac namespace. The rest of this document uses only this default type:
namespace mysvac {
using Json = ::mysvac::json::Json<>;
}Inside the class, six aliases are provided for the JSON subtypes:
| Alias | Default Type | Actual Type |
|---|---|---|
Json::Nul |
std::nullptr_t |
std::nullptr_t |
Json::Bol |
bool |
bool |
Json::Num |
double |
double |
Json::Str |
std::string |
std::basic_string<...,StrAllocator<char>> |
Json::Arr |
std::vector<Json> |
std::vector<Json, VecAllocator<Json>> |
Json::Obj |
std::map<std::string, Json> |
std::map<..,MapAllocator<..>> or std::unordered_map<..,MapAllocator<..>> |
These aliases are dependent on the class template parameters and therefore can only be defined inside the class, not in the namespace.
The default constructor of Json creates a Nul value, but you can also directly initialize it with any of the six JSON types:
Json null_val; // Default constructor, type is Nul
Json bool_val(3.3); // Floating-point initialization, type is Num
Json obj_val = Json::Obj{}; // Initialize directly as an ObjIn addition to the six JSON types, implicit construction is supported from basic arithmetic types, enum types, and const char*.
Enums are treated as integers — do not try to initialize using the json::Type enum, as this will just produce a Num type value:
Json enum_val{ json::Type::eObj }; // Dangerous!
// This will create a Num type, with the numeric value of the enum.Although Json does not support initializer lists directly, implicit construction allows quick creation of objects using Arr and Obj:
Json smp_val = Json::Obj{
{ "key1", 42 },
{ "key2", "value2" },
{ "key3", true },
{ "arr", Json::Arr{ { 2, 3.14, nullptr } } },
{ "obj", Json::Obj{ { "nested_k", "nested_v" } } }
};To initialize an empty array, use Arr{}. For non-empty initialization, double curly braces (Arr{{ ... }}) must be used; otherwise, in some cases, it may be interpreted as a copy or expansion constructor rather than an initializer list.
Avoid
( {} ); parentheses can still cause incorrect interpretation.
You can check the type with type() or is_xxx() functions, or get the type name string with type_name():
smp_val.type(); // Returns json::Type::eObj
json::type_name(smp_val.type()); // Returns "Obj"
smp_val.is_arr(); // Returns falseThere are six is functions: is_arr, is_obj, is_str, is_num, is_bol, and is_nul, corresponding to the six JSON types.
You can reset the value by assignment or with the reset() member function.
reset is a template function that defaults to resetting to Nul, but you can explicitly specify a type, e.g., reset<Json::Obj>() resets to an empty object.
The template parameter for reset must be one of the six JSON types; otherwise, compilation will fail.
This library provides the xxx() member function to obtain a reference to the internal data, where xxx is the same as above in is_xxx.
// Note: Obj's mapped type is still Value
Json& vi_42 = smp_val.obj()["key1"];
// Although it returns a reference, it can also be used for assignment
double i_42 = vi_42.num();
// vi_42.str(); // Type mismatch — throws std::bad_variant_accessJson also provides the [] and at operators.
The difference is that at prevents out-of-range access (throws an exception),
while [] does not check bounds (so Obj can use [] to create a new key-value pair, but Arr out-of-range access is undefined behavior and may cause an assertion failure).
The
constversion of[]is special — it is equivalent to theconstversion ofat, throwing an exception on out-of-range access instead of creating a new key-value pair or crashing.
smp_val["arr"][1].num(); // returns 3.14
smp_val.at("obj").at("nested_k") = nullptr; // modify object, becomes Nul
smp_val["obj"].at("nested_k").is_nul(); // [] and at can be mixedSince arrays and objects may require frequent operations, a series of helper functions is provided. These functions return a boolean indicating success and will not throw exceptions.
| Function | Return Type | Description |
|---|---|---|
size() |
size_t |
Returns number of elements if array or object, otherwise 0 |
empty() |
bool |
Returns whether array or object is empty; other types return true |
contains(key) |
bool |
Checks if an object contains a key; non-object returns false |
erase(key) |
bool |
Deletes key-value pair; returns false if not an object or key not found |
erase(index) |
bool |
Deletes array element; returns false if not an array or index out-of-range |
insert(key, val) |
bool |
Inserts key-value pair; returns false if not an object |
insert(index,val) |
bool |
Inserts array element; returns false if not an array or out-of-range |
push_back(val) |
bool |
Appends to array; returns false if not an array |
pop_back() |
bool |
Removes last array element; returns false if not an array or empty |
Functions like obj()/arr() can only obtain references of the six JSON types.
Therefore, the library also provides to and move templates to obtain the internal value and forcibly convert its type.
The former always copies, while the latter performs move or copy (for simple types, or copy when move is unnecessary).
After calling
move, the original object’s contents become uncertain — it’s best toresetit manually.
auto str_view = smp_val["key2"].to<std::string_view>(); // returns a view of the internal string
auto str = smp_val["key2"].move<std::string>(); // moves the internal string out; now the internal string is empty, and the previous view is invalid
int int_42 = smp_val["key1"].to<int>(); // returns a copy of the internal integerSpecial note: Num data is stored as double.
When converting to an integer (including enum types or types meeting integer template requirements),
the value is rounded to avoid precision issues.
Both to and move support many types.
If conversion fails, a std::runtime_error is thrown.
For this reason, the library also provides xx_if and xx_or versions —
the former returns optional<T>, and the latter returns a specified default value upon failure.
auto opt_str = smp_val["key1"].to_if<std::string>(); // opt_str is std::optional<std::string>; empty on failure without throwing
if (opt_str) std::cout << *opt_str << std::endl; // print string if conversion succeeded
std::string or_str = smp_val["key1"].to_or<std::string>("default"); // returns "default" if conversion failsConversion follows precise rules and a fixed testing order. For details, refer to the GitHub Pages documentation or source code comments.
The library defines three concepts to determine whether a type can be converted
(types that meet none of them cannot use to/move and will fail to compile):
json::convertible<J, T>— typesTthat can be directly converted (includes those satisfyingjson::json_type<J, T>).json::convertible_map<J, T, D>— mappable typesTthat can be indirectly converted, where the key must be a string and the mapped typeDsatisfies concept 1.json::convertible_array<J, T, D>— array typesTthat can be indirectly converted, where the element typeDsatisfies concept 1.
Here, J refers to a specific instantiation of the mysvac::json::Json class template, e.g., mysvac::Json.
As long as a type meets one of these three concepts, it can be converted using the to and move series functions.
We will describe this in detail in the “Custom Type Serialization†section.
Arrays or objects may satisfy multiple concepts at once — this does not affect behavior.
Serialization and deserialization in this library are highly efficient and easy to use.
For deserialization (parsing), use the static member function Json::parse() to convert a string to a Json object.
Note that parse is a class static function (not a namespace function),
since different types may require different parsing implementations.
std::string json_str1 = R"( [ 1, false, null, { "Hello": "World" } ] )";
std::string json_str2 = R"( false )"; // top-level can be any JSON type
Json val1 = Json::parse(json_str1).value_or(nullptr); // parse JSON string
std::cout << val1[1].to<bool>() << std::endl; // prints 0 (boolalpha not specified)Three important notes:
-
JSON parsing failures are common in real-world scenarios and hard to predict, since invalid formats or garbage data are frequent. Therefore, this function returns
std::optional<Json>to avoid exceptions and reduce overhead. There is also an error-type enumeration describing the cause. -
An optional
max_depthparameter (default 256) limits maximum nesting depth. Although the library guarantees overall parsing complexity isO(n)(single-pass), recursion is used for nested structures, so this limit prevents issues such as stack overflow caused by maliciously deep nesting (e.g.,[[[[[[[[]]]]]]]]). -
Besides
string_view, the function also acceptsstd::istreamfor stream-based parsing. Stream parsing is nearly as fast as reading the whole file into a string first, but may use less memory.
For serialization, use the dump/write member functions.
std::string str_ser = val1.dump(); // compact serialization without extra whitespace, returns std::string
std::string str_back;
val1.write(str_back); // append serialization result to std::string
val1.write(std::cout); // output directly to ostreamstr_ser and str_back will contain the same data,
since dump is implemented using write.
Because Json always represents valid JSON, dump always succeeds.
However, write to a stream may fail (e.g., if the file is suddenly closed).
In that case, the function stops immediately when fail() is detected and does not throw —
you must check the stream state manually.
These serialization functions produce compact JSON without extra whitespace.
For more readable output, use the dumpf/writef series (f for “formatâ€),
available in all three forms:
std::string pretty_str = val1.dumpf();
val1.writef(std::cout); // output to ostream
// writef to append to a string is also available, omitted heref-series functions take up to three optional parameters:
indentation spaces per level (default 2), initial indent level (default 0).
They always succeed unless writing to a failed stream.
Deeply nested structures like
[[[[]]]]may cause stack overflow when serializing with indentation, and produce extremely large formatted output. The parser already limits nesting depth, so such structures can only be produced programmatically — avoid creating them.
Json provides an == operator for comparing with another Json.
It first checks whether the types match, then compares values (
std::map and std::vector compare recursively based on their elements).
Json val_arr_1 = Json::Arr{{ 1, 2, 3 }};
Json val_arr_2 = Json::Arr{{ 1, 2, 3 }};
Json val_arr_3 = Json::Arr{{ 1, true, 3 }};
val_arr_1 == val_arr_2; // true
val_arr_1 == val_arr_3; // falseMore uniquely, Json also supports == comparisons with any other type via templates.
If the type is incompatible, it returns false immediately.
If the target type is one of the six JSON types,
it first checks for type match, then compares the actual value.
Otherwise, it attempts to convert the other type to Json
or convert the Json to the target type before comparing.
If neither works, it returns false.
== comparisons are guaranteed not to throw exceptions.
Any custom type that provides a constructor from Json and a type conversion function can interact with JSON data through to/move functions or type conversions, enabling fast serialization and deserialization.
Providing a constructor and a conversion function for
Jsonsatisfies thejson::convertibleconcept.
These functions have some subtle requirements and are not trivial to implement manually. Therefore, this library provides a header file containing only macro definitions, which allows you to easily implement JSON interaction for custom types — including support for move semantics.
You may browse this header file yourself; it is minimal but helps you understand what kinds of types satisfy the conversion criteria.
#define M_MYSVAC_JSON_SIMPLIFY_MACROS // Enable simplified macro function names
#include <mysvac/json_macros.hpp>
// It’s recommended to include this header before other imports, although it only contains macros
import std;
import mysvac.json;
using namespace mysvac;Suppose you have a type defined as:
struct MyData {
int id{};
std::string m_name{};
bool active{};
double m_value{};
};You can add constructors and conversion functions for it via macros, but you must explicitly enable the default constructor:
struct MyData {
int id{};
std::string m_name{};
bool active{false};
double m_value{};
MyData() = default; // Default constructor must exist, implementation can be customized
M_JSON_CV_FUN(MyData, // Conversion function, must be in public scope
M_JSON_CV_MEM(id); // Note the comma after MyData
M_JSON_CV_MAP(name, m_name) // Mapping member `m_name` to JSON key `name`
M_JSON_CV_MEM(active);
M_JSON_CV_MAP(value, m_value)
)
M_JSON_CS_FUN(MyData, // Constructor function, must be in public scope
M_JSON_CS_MEM(id);
M_JSON_CS_MAP(name, m_name);
M_JSON_CS_MEM_OR(active, true, nullptr); // Default value `true`
M_JSON_CS_MAP_OR(value, m_value, 64.0, nullptr); // nullptr means no default for sub-elements
)
};CV stands for Conversion function, and CS stands for Constructor function. Both macros take the type name as the first argument, followed by a comma. The macro parameters specify the JSON fields involved:
MEMmeans member variable name matches JSON key.MAPmeans member variable name differs from JSON key.
You may define your own simpler macros, such as
JCSM,JCSP, etc., to further reduce boilerplate.
Conversion functions are guaranteed to succeed because they rely on existing member variables. However, the constructor might fail because the corresponding JSON keys might be missing (or the root JSON is not an object), so you need to provide default member values.
You’ll notice that constructor macros with OR suffix take two additional parameters for default values. Macros without OR use the member type’s default constructor as the default value, e.g., decltype(name){}.
It is recommended that default values in the constructor match the member variables’ default values, so that a failed JSON conversion results in the same state as default construction.
(Note: The above example sets active's default differently in the constructor, which is discouraged.)
Then you can convert between Json and MyData like this:
Json v_null;
MyData d_null{v_null}; // No data, so all fields use constructor defaults
d_null.active; // true, as specified in constructor
Json v_object{Json::Obj{}};
v_object["id"] = 42;
v_object["name"] = "Test User";
v_object["active"] = false;
v_object["value"] = 128.0;
MyData d_object{v_object}; // Explicit conversion required, no assignment allowed
d_object.m_name == "Test User"; // true
Json v_data{d_object}; // Convert MyData back to JSON object
v_data["id"] == 42; // trueA critical requirement for these macros is that the member variables must support conversion to/from Json.
- Fundamental arithmetic types, enums, the six JSON types, and
Jsonitself inherently satisfy this. - Other custom types must provide conversion and constructor functions like above.
- Containers (e.g.,
std::vector,std::list) composed of types meeting conditions 1 or 2 can be used directly. - Maps (e.g.,
std::map,unordered_map) with keys asstd::stringand values satisfying 1 or 2 can be used directly.
Conditions 1 and 2 correspond to the concept json::convertible. Conditions 3 and 4 correspond to the concepts json::convertible_array and json::convertible_map, respectively.
For example, since MyData now supports conversion, you can nest it within other types for hierarchical JSON objects:
struct MyData2 {
std::string name; // std::string equals json::Str, usable directly
MyData my_data; // MyData has conversion functions, usable directly
std::vector<MyData> data_list; // Lists of convertible types also work (but nested lists don’t)
MyData2() = default;
M_JSON_CV_FUN(MyData2,
M_JSON_CV_MEM(name);
M_JSON_CV_MAP(data, my_data);
M_JSON_CV_MEM(data_list);
)
M_JSON_CS_FUN(MyData2,
M_JSON_CS_MEM(name);
M_JSON_CS_MAP(data, my_data);
M_JSON_CS_MEM_OR(data_list, std::vector<MyData>{}, MyData{}); // member default, sub-element default
)
};Here, you’ll see the fourth parameter in the OR macro: the first is the member default, the second is the default for child elements (used only when the target is an array or map, but not a Json::Arr or Json::Obj). For other types, you can just use nullptr.
For example, if an array is expected but the JSON is not an array, the member default is returned. If JSON is an array but some elements cannot be converted, those elements are replaced by the sub-element default to maintain array length.
You can convert back and forth between the types as follows:
Json v_data2{MyData2{}};
std::println("");
v_data2.writef(std::cout);
std::println("");
v_data2["data"]["id"] = 8848;
v_data2["data"]["name"] = "Mount Everest";
v_data2["data"]["active"] = true;
v_data2["data_list"].push_back(v_data2["data"]);
v_data2["name"] = "name_name";
MyData2 d_data2{v_data2};
M_EXPECT_TRUE(d_data2.my_data.id == 8848); // true
M_EXPECT_TRUE(d_data2.my_data.m_name == "Mount Everest"); // true
M_EXPECT_TRUE(d_data2.data_list.size() == 1); // true
M_EXPECT_TRUE(d_data2.data_list[0].id == 8848); // true
M_EXPECT_TRUE(d_data2.data_list[0].m_name == "Mount Everest"); // true
M_EXPECT_TRUE(d_data2.name == "name_name"); // trueThe
M_EXPECT_TRUEmacro is from the vct-test-unit library and can be ignored if unfamiliar.
As the complexity grows, this final section explains details about lists and maps.
Previously, only six JSON types and basic arithmetic types could directly convert to/from Json. Custom types needed conversion functions and constructors—i.e., only types satisfying json::convertible could convert.
But what about standard types like std::array<int>? The internal int satisfies the condition, but the container itself does not and cannot provide conversion functions.
Hence, this library provides four template functions for conversions between:
- Array types and
Json - Map types and
Json
Which types can use these templates?
- For maps, the key must be
std::stringor convertible to it. - The value type must satisfy
json::convertible(i.e., able to convert directly).
This distinction leads to separate concepts: json::convertible_map and json::convertible_array.
Converting array/map → Json will not lose any elements because all are convertible to JSON. But Json → array/map may lose elements due to invalid data or formats in the JSON.
Thus, if your array or map is not a Json::Arr or Json::Obj, you must provide two default values during conversion:
- The default return value if the JSON structure doesn’t match (e.g., expected array but JSON isn’t an array).
- The default for child elements when partial matches fail (e.g., for
vector<int>if JSON is[1, 2, [], 3], specify the default int to fill for[]).
This explains why macros like M_JSON_CS_MEM_OR and M_JSON_CS_MAP_OR need two default values. If the target type is not an array or map, the child-element default can be anything (commonly nullptr).
This mechanism corresponds to to/move functions internally.
For basic or custom types, conversion is straightforward:
xxx = val.to<MyData>(); // or move<MyData>()For arrays, you must specify the child-element default explicitly:
// Two template parameters: target type, child element type
xxx = val.to<std::vector<MyData>, MyData>(MyData{});
// Template argument deduction also works
xxx = val.to<std::vector<MyData>>(MyData{});The child-element default defaults to Json::Nul (i.e., std::nullptr_t). If your target isn’t an array or object, you don’t need to specify it.
Note that to/move throws exceptions on complete mismatches, so only child-element defaults are specified.
Macros use to_or and move_or which require two default values:
// Second template parameter deduced automatically
xxx = val.to_or<std::vector<MyData>>(std::vector<MyData>{}, MyData{});This covers the basic usage of the library. Although custom type serialization is somewhat complex, you can study the documentation and source code — the entire source is under 2000 lines.
Focus on the implementation of to and move functions and the macro definitions in the headers to get started quickly.
If you find any issues or improvements, please consider submitting an issue or pull request.
mysvac-jsonlib 是一个 C++20 çš„ JSON 库,它æä¾›ç®€æ´ã€é«˜æ•ˆçš„ JSON è§£æžã€æ“作和åºåˆ—化功能。
ä½ å¯ä»¥åœ¨æœ¬ä»“库的 github-pages 页颿‰¾åˆ°è¯¦ç»†çš„æ–‡æ¡£ã€‚
使用 vcpkg å®‰è£…åº“ï¼ˆéœ€è¦æœ€æ–°ç‰ˆæœ¬ï¼‰ï¼š
vcpkg install mysvac-jsonlib
CMakeé…ç½®:
find_package(mysvac-jsonlib CONFIG REQUIRED)
...
target_link_mysvac_jsonlib(main PRIVATE)
在项目ä¸ä½¿ç”¨ï¼š
import std; // ä½¿ç”¨æ ‡å‡†åº“å¤´æ–‡ä»¶æˆ–æ ‡å‡†åº“æ¨¡å—
import mysvac.json; // 导入 mysvac-jsonlib 库
using namespace mysvac; // 使用命å空间,简化代ç 书写JSON å˜åœ¨å…ç§åŸºæœ¬ç±»åž‹ï¼Œæœ¬åº“使用了一个枚举æ¥è¡¨ç¤ºå®ƒä»¬ï¼š
enum class Type{
eNul = 0, ///< null type
eBol, ///< boolean type
eNum, ///< number type
eStr, ///< string type
eArr, ///< array type
eObj ///< object type
};æ¤æžšä¸¾ä½äºŽ mysvac::json 命å空间ä¸ï¼Œè¿™é‡Œé¢è¿˜æœ‰ä¸€ä¸ªé‡è¦çš„ç±»æ¨¡æ¿ Json ,它是一个容器,å¯ä»¥å˜å‚¨ä»»æ„ä¸€ç§ JSON æ•°æ®ç»“构。
template<
bool UseOrderedMap = true,
template<typename U> class VecAllocator = std::allocator,
template<typename U> class MapAllocator = std::allocator,
template<typename U> class StrAllocator = std::allocator
>
class Json;è™½ç„¶ä½ æ— æ³•æŒ‡å®šå¯¹è±¡ï¼ˆæ˜ å°„ï¼‰ã€æ•°ç»„å’Œå—符串的具体实现,但å¯ä»¥ä¸ºå®ƒä»¬æŒ‡å®šå†…å˜åˆ†é…器。
UseOrderedMap: ä½¿ç”¨æœ‰åºæ˜ å°„std::mapï¼Œè¿˜æ˜¯æ— åºçš„std::unordered_map。VecAllocator: 数组类型std::vector使用的分é…器模æ¿ã€‚MapAllocator: å¯¹è±¡ç±»åž‹ï¼ˆæ˜ å°„ï¼‰ä½¿ç”¨çš„åˆ†é…器模æ¿ã€‚StrAllocator: å—符串类型std::basic_string使用的分é…器模æ¿ã€‚
为了方便æ“作,我们在 mysvac 命åç©ºé—´ä¸æä¾›äº†ä¸€ä¸ªé»˜è®¤çš„ Json 别å,本文档åŽç»å†…容仅使用这个默认类型:
namespace mysvac {
using Json = ::mysvac::json::Json<>;
}类内部æä¾›äº†å…ç§ JSON å类型的别å:
| 别å | 默认类型 | 实际类型 |
|---|---|---|
Json::Nul |
std::nullptr_t |
std::nullptr_t |
Json::Bol |
bool |
bool |
Json::Num |
double |
double |
Json::Str |
std::string |
std::basic_string<...,StrAllocator<char>> |
Json::Arr |
std::vector<Json> |
std::vector<Json, VecAllocator<Json>> |
Json::Obj |
std::map<std::string, Json> |
std::map<..,MapAllocator<..>> 或 std::unordered_map<..,MapAllocator<..>> |
这些类型的具体定义与类模æ¿å½¢å‚æœ‰å…³ï¼Œå› æ¤åˆ«ååªèƒ½æ”¾åœ¨ç±»å†…,而éžå‘½å空间ä¸ã€‚
Value çš„é»˜è®¤æž„é€ å‡½æ•°ä¼šåˆ›å»º Nul ç±»åž‹çš„å€¼ï¼Œä½†ä½ å¯ä»¥ç›´æŽ¥é€šè¿‡ä¸Šè¿°å…ç§ç±»åž‹è¿›è¡Œåˆå§‹åŒ–:
Json null_val; // é»˜è®¤æž„é€ ï¼Œç±»åž‹ä¸º Nul
Json bool_val(3.3); // 浮点åˆå§‹åŒ–,类型为 Num
Json obj_val = Json::Obj{}; // 直接使用 Obj åˆå§‹åŒ–除了上述å…ç§ JSON 类型,我们还支æŒä½¿ç”¨åŸºæœ¬ç®—æœ¯ç±»åž‹ã€æžšä¸¾ç±»åž‹å’Œ const char* 进行éšå¼æž„é€ ã€‚
枚举会被视为整数,ä¸è¦è¯•图使用 json::Type 枚举值进行指定åˆå§‹åŒ–,这åªä¼šç”Ÿæˆ Num 类型的值:
Json enum_val{ json::Type::eObj }; // å±é™©
// 这会生æˆä¸€ä¸ª Num 类型的值,具体值å–决于枚举值的整数表示。虽然 Json 䏿”¯æŒåˆå§‹åŒ–列表,但由于éšå¼æž„é€ çš„å˜åœ¨ï¼Œå¯ä»¥é€šè¿‡ Arr å’Œ Obj çš„åˆå§‹åŒ–列表快速创建对象:
Json smp_val = Json::Obj{
{ "key1", 42 },
{"key2", "value2"},
{"key3", true },
{"arr", Json::Arr{ { 2, 3.14, nullptr } } },
{"obj", Json::Obj{ {"nested_k", "nested_v"} } }
};空数组åˆå§‹åŒ–请使用 Arr{} ,éžç©ºåˆå§‹åŒ–必须使用 Arr{ { ... } } åŒé‡å¤§æ‹¬å·ï¼Œå¦åˆ™åœ¨ç‰¹å®šæƒ…å†µä¸‹ä¼šè¢«è®¤ä¸ºæ˜¯æ‹·è´æž„é€ æˆ–æ‰©å®¹æž„é€ è€Œéžåˆå§‹åŒ–列表。
请ä¸è¦ä½¿ç”¨
( {} )ï¼Œå°æ‹¬å·ä¾ç„¶å¯èƒ½è¯†åˆ«é”™è¯¯ã€‚
ä½ å¯ä»¥ç”¨ type() 或 is_xxx() 函数检查类型,或者使用 type_name() 获å–å—符串形å¼çš„类型å:
smp_val.type(); // 返回 json::Type::eObj
json::type_name(smp_val.type()); // 返回 "Obj"
smp_val.is_arr(); // 返回 falseis 共有å…个,分别是 arrã€objã€strã€numã€bol å’Œ nul ,对应å…ç§ JSON 类型。
ä½ å¯ä»¥é€šè¿‡èµ‹å€¼è¯å¥é‡ç½®å†…容,也å¯ä»¥ä½¿ç”¨ reset() æˆå‘˜å‡½æ•°ã€‚
这是个模æ¿å‡½æ•°ï¼Œé»˜è®¤é‡ç½®å›ž Nul ç±»åž‹ï¼Œä½†ä½ å¯ä»¥æ˜¾ç¤ºæŒ‡å®šé‡ç½®ç±»åž‹ï¼Œæ¯”如使用 reset<Json::Obj>() 将内容é‡ç½®ä¸ºä¸€ä¸ªç©ºçš„ Obj 。
reset 的模æ¿å½¢å‚åªèƒ½æ˜¯å…ç§ JSON 类型之一,å¦åˆ™æ— 法通过编译。
本库æä¾›äº† xxx() æˆå‘˜å‡½æ•°ä»¥èŽ·å–内部数æ®çš„引用,xxx 和上é¢çš„ is_xxx 相åŒã€‚
// æ³¨æ„ Obj çš„ mapped ä¾ç„¶æ˜¯ Value 类型
Json& vi_42 = smp_val.obj()["key1"];
// 虽然返回引用,但也å¯ä»¥ç”¨äºŽèµ‹å€¼
double i_42 = vi_42.num();
// vi_42.str(); // 类型ä¸åŒ¹é…,抛出 std::bad_varient_access 异常Json 还æä¾›äº† [] å’Œ at è¿ç®—符,区别在于 at ç¦æ¢ç´¢å¼•越界(抛出异常),而 [] 䏿£€æŸ¥è¶Šç•Œï¼ˆæ‰€ä»¥Objå¯ä»¥ç”¨[]创建新键值对,但Arr越界是未定义行为,å¯èƒ½ç›´æŽ¥æ–言崩溃)。
constçš„[]较为特殊,ç‰ä»·äºŽconstçš„at,越界抛出异常而ä¸ä¼šåˆ›å»ºæ–°é”®å€¼å¯¹æˆ–崩溃。
smp_val["arr"][1].num(); // 返回 3.14
smp_val.at("obj").at("nested_k") = nullptr; // 修改对象,å˜ä¸º Nul
smp_val["obj"].at("nested_k").is_nul(); // [] å’Œ at å¯ä»¥æ··åˆä½¿ç”¨æ•°ç»„å’Œæ˜ å°„ç±»åž‹å¯èƒ½éœ€è¦é¢‘ç¹æ“ä½œï¼Œå› æ¤æˆ‘们æä¾›äº†ä¸€ç³»åˆ—辅助函数。这些函数返回一个布尔值表示æ“ä½œæ˜¯å¦æˆåŠŸï¼Œä¸ä¼šæŠ›å‡ºå¼‚常。
| 函数å | 返回值 | 说明 |
|---|---|---|
size() |
size_t |
æ•°ç»„æˆ–æ˜ å°„åˆ™è¿”å›žå…ƒç´ æ•°ï¼Œå¦åˆ™è¿”回 0 |
empty() |
bool |
è¿”å›žæ•°ç»„æˆ–æ˜ å°„æ˜¯å¦ä¸ºç©ºï¼Œå…¶ä»–类型返回 true |
contains(key) |
bool |
åˆ¤æ–æ˜¯å¦å«æŸä¸ªé”®ï¼Œéžæ˜ 射返回 false |
erase(key) |
bool |
åˆ é™¤é”®å€¼å¯¹ï¼Œéžæ˜ 射或键ä¸å˜åœ¨è¿”回 false |
erase(index) |
bool |
åˆ é™¤æ•°ç»„å…ƒç´ ï¼Œéžæ•°ç»„或索引越界返回 false |
insert(key, val) |
bool |
æ’å…¥é”®å€¼å¯¹ï¼Œéžæ˜ 射返回 false |
insert(index,val) |
bool |
æ’å…¥æ•°ç»„å…ƒç´ ï¼Œéžæ•°ç»„或越界返回 false |
push_back(val) |
bool |
呿•°ç»„æœ«å°¾æ·»åŠ å…ƒç´ ï¼Œéžæ•°ç»„返回 false |
pop_back() |
bool |
åˆ é™¤æ•°ç»„æœ«å°¾å…ƒç´ ï¼Œéžæ•°ç»„或空数组返回 false |
obj()/arr() ç‰å‡½æ•°åªèƒ½èŽ·å–å…ç§å¼•用,所以本库还æä¾›äº† to å’Œ move æ¨¡æ¿æ¥èŽ·å–内部的值并强制转æ¢ç±»åž‹ã€‚
å‰è€…必然是拷è´ï¼ŒåŽè€…是移动或拷è´ï¼ˆç®€å•ç±»åž‹ï¼Œæˆ–è€…æ— éœ€ç§»åŠ¨æ—¶è¿›è¡Œæ‹·è´ï¼‰ã€‚
调用
moveåŽéš¾ä»¥ç¡®å®šåŽŸå¯¹è±¡çš„å†…å®¹ï¼Œæœ€å¥½ä¸»åŠ¨reset。
auto str_view = smp_val["key2"].to<std::string_view>(); // 返回内部å—符串的视图
auto str = smp_val["key2"].move<std::string>(); // 将内部å—符串移动了出æ¥ï¼ŒçŽ°åœ¨å†…éƒ¨å˜ä¸ºç©ºå—符串,之å‰çš„视图ä¸å†å¯ç”¨
int int_42 = smp_val["key1"].to<int>(); // 返回内部整数的拷è´ç‰¹åˆ«æ³¨æ„,Num æ•°æ®ä½¿ç”¨ double å˜å‚¨ï¼Œå› æ¤åœ¨è½¬æ¢æˆæ•´æ•°æ—¶ï¼ˆæžšä¸¾ç±»åž‹ï¼Œæˆ–ç¬¦åˆæ•´æ•°æ¨¡æ¿è¦æ±‚的类型),会四èˆäº”入,é¿å…精度问题。
注æ„,to å’Œ move 支æŒå¾ˆå¤šç±»åž‹ï¼Œè½¬æ¢å¤±è´¥ä¼šæŠ›å‡º std::runtime_error 异常。
为æ¤ï¼Œæˆ‘们还æä¾›äº† xx_if å’Œ xx_or 版本,å‰è€…返回 optional<T> ,åŽè€…则是在失败时返回指定的默认值。
auto opt_str = smp_val["key1"].to_if<std::string>(); // opt_str 是 std::optional<std::string> ,转æ¢å¤±è´¥ä¸ºç©ºï¼Œä½†ä¸æŠ›å‡ºå¼‚常
if(opt_str) std::cout << *opt_str << std::endl; // å¦‚æžœè½¬æ¢æˆåŠŸï¼Œè¾“å‡ºå—符串
std::string or_str = smp_val["key1"].to_or<std::string>("default"); // 如果转æ¢å¤±è´¥ï¼Œè¿”回 "default"转æ¢å…·æœ‰éžå¸¸å‡†ç¡®çš„规则与测试顺åºï¼Œè¯¦ç»†å†…容请å‚考 github-pages 文档,或æºç 注释。
本库æä¾›äº†ä¸‰ä¸ªæ¦‚念用于查询类型是å¦å¯èƒ½è¢«è½¬æ¢ï¼šï¼ˆéƒ½ä¸æ»¡è¶³çš„æ— 法使用to/move模æ¿ï¼Œæ— 法通过编译)
json::convertible<J, T>å¯èƒ½ç›´æŽ¥è½¬æ¢æˆåŠŸçš„ç±»åž‹T,这包å«äº†æ»¡è¶³json::json_type<J, T>的类型。json::convertible_map<J, T,D>å¯èƒ½é—´æŽ¥è½¬æ¢æˆåŠŸçš„æ˜ å°„ç±»åž‹T,键必须是å—符串,值(mapped)是D类型且满足æ¡ä»¶ 1 。json::convertible_array<J, T,D>å¯èƒ½é—´æŽ¥è½¬æ¢æˆåŠŸçš„æ•°ç»„ç±»åž‹T,内部值是D类型且满足æ¡ä»¶ 1 。
这里的 J 是指 mysvac::json::Json 类模æ¿çš„具体实例化类型,比如 mysvac::Json 。
åªè¦ç±»åž‹æ»¡è¶³ä¸‰ç§æ¦‚念之一,就å¯ä»¥ä½¿ç”¨ to å’Œ move 系列函数进行转æ¢ã€‚我们会在åŽç»çš„“自定义类型åºåˆ—化â€éƒ¨åˆ†è¯¦ç»†ä»‹ç»ã€‚
æ•°ç»„æˆ–æ˜ å°„å¯èƒ½åŒæ—¶æ»¡è¶³å¤šç§æ¦‚念,但这ä¸å½±å“效果。
本库的åºåˆ—化和ååºåˆ—化éžå¸¸é«˜æ•ˆä¸”容易使用。
首先是ååºåˆ—化,将å—符串转æ¢ä¸º Json 对象,使用 Json::parse() 函数。
æ³¨æ„ parse æ˜¯ç±»çš„é™æ€æˆå‘˜å‡½æ•°ï¼Œè€Œéžå‘½å空间ä¸çš„å‡½æ•°ï¼Œå› ä¸ºä¸åŒç±»åž‹éœ€è¦ä¸åŒçš„è§£æžå‡½æ•°ã€‚
std::string json_str1 = R"( [ 1, false, null, { "Hello": "World" } ] )";
std::string json_str2 = R"( false )"; // å…许顶层类型是任一 JSON 类型
Json val1 = Json::parse(json_str1).value_or( nullptr ); // è§£æž JSON å—符串
std::cout << val1[1].to<bool>() << std::endl; // 输出 0 (没有指定 boolaplha)这里还需è¦è¯´æ˜Žä¸‰ä»¶äº‹ï¼š
-
JSON 文件解æžå¤±è´¥åœ¨å®žé™…应用ä¸å¾ˆå¸¸è§ä¸”éš¾ä»¥é¢„æ–™ï¼Œå› ä¸ºå¾ˆå®¹æ˜“æœ‰ä¸€äº›æ ¼å¼é”™è¯¯æˆ–垃圾数æ®ã€‚ å› æ¤æœ¬åº“çš„ååºåˆ—化函数返回
std::optional<Json>,从而é¿å…使用异常机制,å‡å°å¼€é”€ã€‚åŽè€…是一个æè¿°é”™è¯¯ç±»åž‹çš„æžšä¸¾ã€‚ -
æ¤å‡½æ•°è¿˜å…·æœ‰ä¸€ä¸ªå¯é€‰å‚æ•°
max_depth(默认是 256),用于é™åˆ¶è§£æžçš„æœ€å¤§ï¼ˆåµŒå¥—)深度。 本库虽然ä¿è¯æ€»è§£æžå¤æ‚度是O(n)çš„ï¼ˆä¸¥æ ¼å•æ¬¡é历),但使用了递归æ¥å¤„ç†åµŒå¥—ç»“æž„ï¼Œå› æ¤éœ€è¦ç”¨å®ƒé¿å…æŸäº›åžƒåœ¾æ•°æ®çš„问题(比如过长的[[[[[[[[]]]]]]]]在递归时å¯èƒ½å¯¼è‡´æ ˆæº¢å‡ºï¼‰ã€‚ -
æ¤å‡½æ•°é™¤äº†
string_viewå¤–ï¼Œè¿˜èƒ½ä¼ å…¥std::istream进行æµå¼è§£æžã€‚ æµå¼è§£æžæ–‡ä»¶çš„æ•ˆçŽ‡å‡ ä¹Žç‰åŒäºŽå…ˆå°†æ–‡ä»¶å…¨éƒ¨è¯»å…¥ string å†ç”¨ string è§£æžï¼Œä½†å†…å˜å 用å¯èƒ½æ›´å°‘。
ç„¶åŽæ˜¯åºåˆ—化,使用 Json 对象的 dump/write æˆå‘˜å‡½æ•°ã€‚
std::string str_ser = val1.dump(); // ä¸å«æ— 效空白å—符的高效åºåˆ—化,返回 std::string
std::string str_back;
val1.write( str_back ); // å°†åºåˆ—化结果写入 std::string 的末尾
val1.write( std::cout ); // å°†åºåˆ—化结果直接输出到 `ostream` ä¸çŽ°åœ¨ str_ser å’Œ str_back çš„å†…å®¹å®Œå…¨ä¸€æ ·ï¼Œå› ä¸º dump 就是用 write 实现的。
由于 Json 必然是有效的 JSON æ•°æ®ï¼Œå› æ¤ dump å¿…ç„¶æˆåŠŸã€‚
ä¸è¿‡ write çš„æµæ“作ä¸ä¸€å®šæˆåŠŸï¼ˆæ¯”å¦‚æ–‡ä»¶çªç„¶å…³é—)ã€å‡½æ•°æ£€æµ‹åˆ°æµçš„状æ€ä¸º fail() åŽä¼šç«‹å³è¿”回,但ä¸ä¼šæŠ›å‡ºå¼‚常,需è¦ä½ 自行检查æµçš„状æ€ã€‚
上é¢çš„三个åºåˆ—化函数都是高效的紧凑åºåˆ—化,ä¸å«æ— 效空白å—符。
ä½†ä½ å¯ä»¥ä½¿ç”¨ dumpf/writef 系列函数æ¥èŽ·å¾—æ›´æ˜“è¯»çš„æ ¼å¼åŒ–输出,f 指 format ï¼Œå®ƒåŒæ ·åŒ…å«ä¸‰ç§å½¢å¼ï¼š
std::string pretty_str = val1.dumpf();
val1.writef( std::cout ); // 输出到 `ostream`
// 还有 writef 写入å—符串末尾,æ¤å¤„çœç•¥f 系列有三个å¯é€‰å‚æ•°ï¼Œä¾æ¬¡æ˜¯ â€œå•æ¬¡ç¼©è¿›ç©ºæ ¼æ•°ï¼ˆé»˜è®¤ 2)â€ï¼Œâ€œåˆå§‹ç¼©è¿›æ¬¡æ•°ï¼ˆé»˜è®¤ 0)â€ã€‚
è¿™äº›å‡½æ•°åŒæ ·å¿…ç„¶æˆåŠŸï¼ˆé™¤éžå†™å…¥æµä½†æµçжæ€å¼‚常)。
一些特殊数æ®å¦‚
[[[[]]]]è¿™æ ·çš„æ·±åº¦åµŒå¥—ç»“æž„ï¼Œåºåˆ—化时å¯èƒ½å¯¼è‡´é€’å½’è¿‡æ·±æ ˆæº¢å‡ºï¼Œä¸”å¸¦ç¼©è¿›çš„æ ¼å¼åŒ–文本会éžå¸¸å¤§ã€‚ 我们在ååºåˆ—化函数ä¸é™åˆ¶äº†åµŒå¥—æ·±åº¦ï¼Œå› æ¤ä½ åªå¯èƒ½åœ¨ç¨‹åºä¸ä¸»åŠ¨æž„é€ è¿™æ ·çš„ç‰¹æ®Šæ•°æ®è€Œä¸å¯èƒ½ä»Žå¤–部输入,å¯ä»¥ä¸»åЍé¿å…æž„é€ æ¤ç±»æ•°æ®ã€‚
Json 类型æä¾›äº†å’Œ Json 进行比较的 == è¿ç®—符,它首先判æ–类型是å¦ç›¸åŒï¼Œç„¶åŽè°ƒç”¨å†…部的 == 进行比较( std::map å’Œ std::vector 的比较基于åå…ƒç´ å†…å®¹ï¼Œä»Žè€Œå®žçŽ°é€’å½’æ¯”è¾ƒï¼‰ã€‚
Json val_arr_1 = Json::Arr{{ 1, 2, 3 }};
Json val_arr_2 = Json::Arr{{ 1, 2, 3 }};
Json val_arr_3 = Json::Arr{{ 1, true, 3 }};
val_arr_1 == val_arr_2; // true
val_arr_1 == val_arr_3; // falseæ›´åŠ ç‰¹æ®Šçš„æ˜¯ï¼Œ Json 还通过模æ¿å‡½æ•°å®žçŽ°äº†å’Œå…¶ä»–ä»»æ„类型的 == 比较。
ä¸å…¼å®¹çš„类型直接返回 false ï¼Œå¦‚æžœç›®æ ‡æ˜¯å…ç§ JSON 类型之一,则先测试类型是å¦åŒ¹é…ï¼Œç„¶åŽæ¯”较具体值。
å¦åˆ™ï¼Œå°è¯•将对象转æ¢ä¸º Json ,或者将 Json 转æ¢ä¸ºç›®æ ‡ç±»åž‹ç„¶åŽæ¯”较。都ä¸åŒ¹é…则返回 false 。
== æ“作必然æˆåŠŸï¼Œä¸ä¼šæŠ›å‡ºå¼‚常。
任何自定义类型,åªè¦æä¾›é’ˆå¯¹ Json çš„æž„é€ å‡½æ•°å’Œç±»åž‹è½¬æ¢å‡½æ•°ï¼Œå°±èƒ½é€šè¿‡ to/move 或者类型转æ¢ç‰æ–¹å¼ä¸Ž JSON æ•°æ®äº¤äº’,从而实现快速的åºåˆ—化和ååºåˆ—化。
æä¾›äº†é’ˆå¯¹
Jsonæž„é€ å‡½æ•°å’Œç±»åž‹è½¬æ¢å‡½æ•°ï¼Œå°±æ»¡è¶³äº†json::convertible概念。
è¿™äº›å‡½æ•°è¿˜æœ‰ä¸€äº›ç»†èŠ‚è¦æ±‚,它们的实现并ä¸è½»æ¾ï¼Œå› æ¤æœ¬åº“æä¾›äº†ä¸€ä¸ªä»…包å«å®å®šä¹‰çš„å¤´æ–‡ä»¶ï¼Œè®©ä½ å¯ä»¥è½»æ¾å®žçŽ°è‡ªå®šä¹‰ç±»åž‹ä¸Ž JSON 的交互,它甚至支æŒç§»åЍè¯ä¹‰ã€‚
ä½ å¯ä»¥è‡ªè¡Œæµè§ˆæ¤å¤´æ–‡ä»¶ï¼Œå®ƒçš„内容很少,但å¯ä»¥è®©ä½ äº†è§£ä»€ä¹ˆç±»åž‹èƒ½å¤Ÿæ»¡è¶³è½¬æ¢æ¡ä»¶ã€‚
#define M_MYSVAC_JSON_SIMPLIFY_MACROS // 定义å®ï¼Œä»¥å¯ç”¨ç®€åŒ–çš„å®å‡½æ•°å
#include <mysvac/json_macros.hpp>
// 建议将所有头文件放在所有 import 之å‰ï¼Œè™½ç„¶æ¤æ–‡ä»¶ä»…å«å®å®šä¹‰
import std;
import mysvac.json;
using namespace mysvac;å‡è®¾ä½ çŽ°åœ¨æœ‰è¿™æ ·ä¸€ä¸ªç±»åž‹ï¼š
struct MyData{
int id{};
std::string m_name{};
bool active{};
double m_value{};
};ç„¶åŽä½ å¯ä»¥åƒä¸‹é¢è¿™æ ·ï¼Œé€šè¿‡å®å®šä¹‰ä¸ºå…¶æ·»åŠ æž„é€ å‡½æ•°å’Œè½¬æ¢å‡½æ•°ï¼Œä½†éœ€è¦æ˜¾å¼å¯ç”¨é»˜è®¤æž„é€ ï¼š
struct MyData{
int id{};
std::string m_name{};
bool active{false};
double m_value{};
MyData() = default; // å¿…é¡»å˜åœ¨é»˜è®¤æž„é€ ï¼Œå†…å®¹å¯ä»¥è‡ªå®šä¹‰
M_JSON_CV_FUN( MyData, // 转æ¢å‡½æ•°ï¼Œå¿…须在 public 作用域
M_JSON_CV_MEM( id ); // 注æ„,MyData åŽé¢å¿…须有 `,`
M_JSON_CV_MAP( name, m_name ) // ä½†æ˜¯å‰©ä½™çš„å—æ®µåŽé¢ä¸èƒ½æœ‰é€—å· `,` ï¼Œåˆ†å· `;` 则是å¯é€‰çš„
M_JSON_CV_MEM( active )
M_JSON_CV_MAP( value, m_value )
)
M_JSON_CS_FUN( MyData, // æž„é€ å‡½æ•°ï¼Œå¿…é¡»åœ¨ public 作用域
M_JSON_CS_MEM( id )
M_JSON_CS_MAP( name, m_name )
M_JSON_CS_MEM_OR( active, true, nullptr ) // 默认值是 `true`
M_JSON_CS_MAP_OR( value, m_value, 64.0, nullptr ) // nullptr 表示æ¤ç±»åž‹ä¸éœ€è¦åå…ƒç´ é»˜è®¤å€¼
)
};CV 的是指 Conversion 转æ¢å‡½æ•°ï¼Œè€Œ CS 是指 Constructor æž„é€ å‡½æ•°ã€‚å®ƒä»¬çš„ç¬¬ä¸€ä¸ªå‚æ•°éƒ½æ˜¯ç±»åž‹å,åŽé¢éœ€è¦ä¸€ä¸ª , 分隔符。
ç„¶åŽé€šè¿‡å¯¹åº”çš„å®å®šä¹‰æŒ‡å®š JSON 转æ¢ä¸éœ€è¦çš„å—æ®µï¼ŒMEM 是指æˆå‘˜å˜é‡å与 JSON é”®å相åŒï¼ŒMAP 是指æˆå‘˜å˜é‡å与 JSON é”®åä¸åŒï¼ˆæ¯”如键是 name ,而æˆå‘˜å˜é‡å是 m_name)。
ä½ å¯ä»¥é€‰æ‹©è‡ªè¡Œå®šä¹‰ä¸€äº›ç®€åŒ–å®ï¼Œæ¯”如
JCSMJCSPç‰ç‰ï¼Œé«˜åº¦ç®€åŒ–书写。
转æ¢å‡½æ•°æ˜¯å¿…ç„¶æˆåŠŸçš„ï¼Œå› ä¸ºéœ€è¦çš„æ•°æ®éƒ½æ˜¯æˆå‘˜å˜é‡ã€‚
ä½†æ˜¯æž„é€ å‡½æ•°ä¸çš„æˆå‘˜èµ‹å€¼å¯èƒ½ä¼šå¤±è´¥ï¼Œå› 为 Json ä¸å¯èƒ½ä¸å˜åœ¨å¯¹åº”的键(甚至 Json æ ¹æœ¬ä¸æ˜¯ Obj ç±»åž‹ï¼‰ï¼Œå› æ¤éœ€è¦æŒ‡å®šæˆå‘˜é»˜è®¤å€¼ã€‚
ä½ ä¼šçœ‹åˆ°æž„é€ å‡½æ•°ï¼ˆCS)的å®ï¼Œéƒ¨åˆ†å¸¦æœ‰ OR åŽè€…ï¼Œå®ƒä»¬å¤šäº†ä¸¤ä¸ªå‚æ•°ï¼Œç¬¬ä¸€ä¸ªå‚数就是默认值。
而没有 OR çš„å®å¹¶éžæ²¡æœ‰é»˜è®¤å€¼ï¼Œè€Œæ˜¯å°†å¯¹åº”ç±»åž‹çš„é»˜è®¤æž„é€ ä½œä¸ºé»˜è®¤å€¼ï¼Œå³ decltype(name){} 。
ä½œè€…å»ºè®®æ˜¯ï¼Œå—æ®µçš„默认值请和æˆå‘˜å˜é‡çš„é»˜è®¤å€¼ä¿æŒä¸€è‡´ï¼Œå› 为我们希望从 Json 转æ¢å¤±è´¥çš„结果ç‰äºŽé»˜è®¤æž„é€ å‡½æ•°çš„æ•ˆæžœã€‚
ï¼ˆä¸Šé¢ active 的默认值就和 CS 䏿Œ‡å®šçš„ä¸ä¸€æ ·ï¼Œä¸æŽ¨èè¿™ç§å†™æ³•)
ç„¶åŽä½ å°±å¯ä»¥åƒä¸‹é¢è¿™æ ·ï¼Œè®© Json å’Œ MyData 互相转æ¢äº†ï¼š
Json v_null;
MyData d_null{ v_null }; // ä»€ä¹ˆéƒ½æ²¡æœ‰ï¼Œå› æ¤å…¨éƒ¨å—段都是 CS ä¸çš„默认值
d_null.active; // trueï¼Œå› ä¸º CS 函数指定了默认值为 true
Json v_object{ Json::Obj{} };
v_object["id"] = 42;
v_object["name"] = "Test User";
v_object["active"] = false;
v_object["value"] = 128.0;
MyData d_object{ v_object }; // 必须显å¼è½¬æ¢ï¼Œä¸èƒ½ç”¨ `=` æž„é€
d_object.m_name == "Test User"; // true
Json v_data{ d_object }; // å°† MyData 转æ¢ä¸º JSON 对象
v_data["id"] == 42; // trueä½¿ç”¨è¿™ä¸¤ä¸ªå®æœ‰ä¸€ä¸ªéžå¸¸é‡è¦çš„è¦æ±‚,å³éœ€è¦è½¬æ¢çš„æˆå‘˜å˜é‡å¿…须支æŒä¸Ž Json 类型的转æ¢ã€‚
- å¯¹äºŽåŸºæœ¬ç®—æœ¯ç±»åž‹ã€æžšä¸¾ç±»åž‹ã€å…ç§ JSON 类型和
Jsonè‡ªèº«ï¼Œå¿…ç„¶æ»¡è¶³è¦æ±‚。 - 对于其他自定义类类型,需è¦åƒä¸Šé¢ä¸€æ ·æä¾›è½¬æ¢å‡½æ•°å’Œæž„é€ å‡½æ•°ã€‚
- 对于满足æ¡ä»¶ 1 或 2 的类型构æˆçš„列表(如
std::vector,std::listç‰ï¼‰ï¼Œå¯ä»¥ç›´æŽ¥ä½¿ç”¨ã€‚ - 对于满足æ¡ä»¶ 1 或 2 的类型构æˆçš„æ˜ 射(如
std::map,unordered_mapç‰ï¼‰ï¼Œåœ¨é”®ä¸ºstd::string时也å¯ä»¥ç›´æŽ¥ä½¿ç”¨ã€‚
æ¡ä»¶ 1 å’Œ 2 指的是概念 json::convertible ,而æ¡ä»¶ 3 å’Œ 4 指的是概念 json::convertible_array å’Œ json::convertible_map 。
比如,现在的 MyData 类型已ç»é€šè¿‡å®å®šä¹‰æä¾›äº†è½¬æ¢å‡½æ•°å’Œæž„é€ å‡½æ•°ï¼Œæ»¡è¶³æ¡ä»¶ 2 。
å› æ¤ä½ å¯ä»¥åœ¨å…¶ä»–类型ä¸ç›´æŽ¥ä½¿ç”¨å®ƒï¼Œç„¶åŽå®žçŽ°åµŒå¥—çš„ JSON 对象:
struct MyData2 {
std::string name; // std::string ç‰äºŽ json::Strï¼Œå› æ¤å¯ä»¥ç›´æŽ¥ä½¿ç”¨
MyData my_data; // MyData å·²ç»æœ‰è½¬æ¢å‡½æ•°å’Œæž„é€ å‡½æ•°ï¼Œå› æ¤å¯ä»¥ç›´æŽ¥ä½¿ç”¨
std::vector<MyData> data_list; // 能够直接使用的类型构æˆçš„列表也能直接使用(但å†å¥—一层列表就ä¸è¡Œäº†ï¼‰
MyData2() = default;
M_JSON_CV_FUN( MyData2,
M_JSON_CV_MEM( name )
M_JSON_CV_MAP( data, my_data )
M_JSON_CV_MEM( data_list )
)
M_JSON_CS_FUN( MyData2,
M_JSON_CS_MEM( name )
M_JSON_CS_MAP( data, my_data )
M_JSON_CS_MEM_OR( data_list, std::vector<MyData>{}, MyData{} ) // å˜é‡å,默认值,内部åå…ƒç´ çš„é»˜è®¤å€¼
)
};å¯ä»¥çœ‹åˆ°æˆ‘们用到了 OR å®çš„ç¬¬å››ä¸ªå‚æ•°ã€‚ç¬¬ä¸‰ä¸ªå‚æ•°æ˜¯å—æ®µæœ¬èº«çš„é»˜è®¤å€¼ï¼Œç¬¬å››ä¸ªå‚æ•°æ˜¯åå…ƒç´ çš„é»˜è®¤å€¼ã€‚
ç¬¬å››ä¸ªå‚æ•°ä»…åœ¨ç›®æ ‡æ˜¯æ•°ç»„æˆ–è€…æ˜ å°„ç±»åž‹ï¼ˆä¸”éž Json::Arr/Obj ï¼‰æ—¶æ‰æœ‰ç”¨ï¼Œå…¶ä»–时候å¯ä»¥éšæ„填写,通常用 nullptr 。
æ¯”å¦‚ä½ éœ€è¦æ•°ç»„,但是 Json å†…éƒ¨ä¸æ˜¯æ•°ç»„ï¼Œå°±ä¼šè¿”å›žç¬¬ä¸‰ä¸ªå—æ®µçš„默认值。
Json ä¹Ÿæ˜¯æ•°ç»„ï¼Œä½†æ˜¯å†…éƒ¨åªæœ‰éƒ¨åˆ†å…ƒç´ 能够转æˆä½ 需è¦çš„ç±»åž‹ï¼Œé‚£ä¹ˆå…¶ä»–å…ƒç´ ä¼šç”¨ç¬¬å››ä¸ªå‚æ•°çš„默认值填充,ä¿è¯æ•°ç»„长度一致。
ç„¶åŽä½ å¯ä»¥åƒä¸‹é¢è¿™æ ·åœ¨ä¸¤ç§ç±»åž‹ä¹‹é—´æ¥å›žåˆ‡æ¢ï¼š
Json v_data2{ MyData2{} };
std::println("");
v_data2.writef( std::cout );
std::println("");
v_data2["data"]["id"] = 8848;
v_data2["data"]["name"] = "Mount Everest";
v_data2["data"]["active"] = true;
v_data2["data_list"].push_back( v_data2["data"] );
v_data2["name"] = "name_name";
MyData2 d_data2{ v_data2 };
M_EXPECT_TRUE( d_data2.my_data.id == 8848 ); // true
M_EXPECT_TRUE( d_data2.my_data.m_name == "Mount Everest" ); // true
M_EXPECT_TRUE( d_data2.data_list.size() == 1 ); // true
M_EXPECT_TRUE( d_data2.data_list[0].id == 8848 ); // true
M_EXPECT_TRUE( d_data2.data_list[0].m_name == "Mount Everest" ); // true
M_EXPECT_TRUE( d_data2.name == "name_name" ); // true这里的
M_EXPECT_TRUE使用的是 vct-test-unit åº“ï¼Œä½ å¯ä»¥ä¸ç”¨åœ¨æ„。
内容å˜å¾—è¶Šæ¥è¶Šå¤æ‚了,这里作为最åŽä¸€éƒ¨åˆ†ï¼Œå°†ä»‹ç»åˆ—è¡¨å’Œæ˜ å°„çš„å®žçŽ°ç»†èŠ‚ã€‚
æˆ‘ä»¬ä¹‹å‰æåˆ°ï¼ŒåŽŸå…ˆåªæœ‰å…ç§ JSON 类型和基本ç
9444
®—术类型是能够直接和 Json 转æ¢çš„,而自定义类型需æä¾›è½¬æ¢å‡½æ•°å’Œæž„é€ å‡½æ•°ã€‚
ä¹Ÿå°±æ˜¯åªæœ‰æ»¡è¶³ json::convertible 概念的类型æ‰èƒ½ç›´æŽ¥è½¬æ¢ã€‚
ä½†æ˜¯ï¼Œåƒ array<int> è¿™ç§æ ‡å‡†åº“æä¾›çš„类型怎么办呢?它很常用,内部的 int æ»¡è¶³äº†è½¬æ¢æ¡ä»¶ï¼Œä½†æ•´ä½“并䏿»¡è¶³ï¼Œåˆæ— 法让它æä¾›è½¬æ¢å‡½æ•°å’Œæž„é€ å‡½æ•°ã€‚
å› æ¤ï¼Œæœ¬åº“为 Json æä¾›äº†å››ä¸ªæ¨¡æ¿å‡½æ•°ï¼Œåˆ†åˆ«å¯¹åº” 数组类型->Json å’Œ Json->数组类型 ä»¥åŠ æ˜ å°„ç±»åž‹->Json å’Œ Json->æ˜ å°„ç±»åž‹ 的转æ¢ã€‚
什么类型能够用这些模æ¿å‘¢ï¼Ÿé¦–å…ˆæ˜ å°„ç±»åž‹çš„é”®å¿…é¡»æ˜¯ std::string 或者å¯ä»¥è½¬æ¢ä¸º std::string 的类型。
最é‡è¦çš„æ˜¯å†…éƒ¨çš„å€¼ç±»åž‹ï¼Œè¦æ±‚是 json::convertible ï¼Œå› ä¸ºè¿™äº›ç±»åž‹èƒ½å¤Ÿç›´æŽ¥è½¬æ¢ã€‚
这就是为什么会有两个独立的概念 json::convertible_map 和 json::convertible_array 。
数组/æ˜ å°„->Json是ä¸ä¼šé—æ¼ä»»ä½•å…ƒç´ çš„ï¼Œå› ä¸ºæ‰€æœ‰å…ƒç´ éƒ½èƒ½è¢« Json 接å—。
但是å之则ä¸ç„¶ï¼ŒJson->数组/æ˜ å°„ å¯èƒ½ä¼šä¸¢å¤±ä¸€äº›å…ƒç´ ï¼Œå› ä¸º Json å¯èƒ½æœ‰å„ç§å¥‡æ€ªçš„æ•°æ®å’Œæ ¼å¼ã€‚
å› æ¤ï¼Œå¦‚æžœä½ çš„æ•°ç»„å’Œæ˜ å°„ä¸æ˜¯åŸºæœ¬ç±»åž‹é‡Œçš„ Json::Arr å’Œ Json::Obj ï¼Œé‚£ä¹ˆåœ¨è½¬æ¢æ—¶å¿…é¡»æä¾›ä¸¤ä¸ªé»˜è®¤å€¼ï¼š
-
完全ä¸åŒ¹é…时返回的默认结果。比如需è¦è½¬æ¢æˆæ•°ç»„,但是
Jsonå†…éƒ¨ä¸æ˜¯æ•°ç»„,则直接返回æ¤é»˜è®¤å€¼ã€‚ -
能够匹é…ç±»åž‹ï¼Œä½†æ˜¯å±€éƒ¨å…ƒç´ ä¸åŒ¹é…æ—¶å¡«å……çš„åå…ƒç´ é»˜è®¤å€¼ã€‚æ¯”å¦‚éœ€è¦
vector<int>,但是Json䏿˜¯[1, 2, [], 3]ï¼Œä½ éœ€è¦æŒ‡å®šé‡åˆ°[]这些ä¸åŒ¹é…å…ƒç´ æ—¶å¡«å……çš„é»˜è®¤æ•´æ•°ã€‚
这也就是为什么 M_JSON_CS_MEM_OR å’Œ M_JSON_CS_MAP_OR å®å®šä¹‰éœ€è¦ä¸¤ä¸ªé»˜è®¤å€¼ã€‚
ä¸è¿‡ï¼Œå¦‚æžœä½ è½¬æ¢çš„ç±»åž‹ä¸æ˜¯æ•°ç»„æˆ–è€…æ˜ å°„ï¼Œæœ€åŽè¿™ä¸ªåå…ƒç´ é»˜è®¤å€¼å¯ä»¥ä»»æ„å¡«å†™ï¼Œä¸Šé¢æˆ‘们就使用过 nullptr 作为默认值。
æ¤å†…容在代ç ä¸å®žé™…对应 to/move 系列函数。
对于基本类型或者自定义类型的数æ®ï¼Œå¯ä»¥åƒä¹‹å‰ä¸€æ ·ç›´æŽ¥è½¬æ¢ï¼š
xxx = val.to<MyData>(); // 或者 move<MyData>()但如果需è¦è½¬æ¢æˆæ•°ç»„ï¼Œå°±éœ€è¦æ˜¾å¼æŒ‡å®šåå…ƒç´ é»˜è®¤å€¼ï¼š
// å®žé™…æ¨¡æ¿æœ‰ä¸¤ä¸ªå‚æ•°ï¼Œç¬¬ä¸€ä¸ªæ˜¯ç›®æ ‡ç±»åž‹ï¼Œç¬¬äºŒä¸ªæ˜¯å¡«å……åå…ƒç´ çš„ç±»åž‹
xxx = val.to<std::vector<MyData>, MyData>( MyData{} );
// å¯ä»¥æ ¹æ®å‡½æ•°å‚数自动推导第二个模æ¿å‚æ•°
xxx = val.to<std::vector<MyData>>( MyData{} ); 第二个模æ¿å‚数默认是 Json::Nul ï¼Œå³ std:::nullptr_t 。如果转æ¢ç›®æ ‡ä¸æ˜¯æ•°ç»„或对象,完全ä¸éœ€è¦æ·»åŠ å®ƒã€‚
注æ„, to/move 在完全ä¸åŒ¹é…æ—¶ç›´æŽ¥æŠ›å‡ºå¼‚å¸¸ï¼Œæ‰€ä»¥æˆ‘ä»¬åªæŒ‡å®šäº†åå…ƒç´ é»˜è®¤å€¼ã€‚
而å®å®šä¹‰å®žé™…ç”± to_or å’Œ move_or å®žçŽ°ï¼Œå› æ¤éœ€è¦ä¸¤ä¸ªé»˜è®¤å€¼ï¼š
// 第二个模æ¿å‚数使用自动推导
xxx = val.to_or<std::vector<MyData>>( std::vector<MyData>{} , MyData{} ); 以上就是本库的基本使用,虽然自定义类型åºåˆ—åŒ–çš„éƒ¨åˆ†æ¯”è¾ƒå¤æ‚ï¼Œä½†ä½ å¯ä»¥è‡ªè¡Œé˜…读文档和æºç ,本库的æºç 的有效行数其实éžå¸¸å°‘(ä¸è¶³ 2000 行)。
é‡ç‚¹è§‚察 to å’Œ move 的实现,以åŠå¤´æ–‡ä»¶ä¸çš„å®å®šä¹‰ï¼Œä½ 应该能很快上手。
å¦‚æžœä½ å‘现的本库的任何问题,或者å¯ä¼˜åŒ–的地方,欢迎æäº¤ issue 或 PR。










