概述
- C++17 标准
- 仅标准库,跨平台
- 代码不到1000行,轻量
- 性能较好
- 操作非常简单
- 序列化、反序列化、美化、增删改查
- 移动语义与异常处理支持
- 支持从vcpkg安装库
文档
↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓
C++Doxygen文档
↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑
应用示例
0. 导入库与头文件
你可以直接下载src
和include
中的两个代码文件,放到项目中使用。
也可以作为第三方库导入(默认静态库),方式如下:
# 请先使用 'git pull' 更新vcpkg端口文件
# 全局模式
vcpkg install mysvac-jsonlib
# 清单模式
vcpkg add port mysvac-jsonlib
# CmakeLists.txt
find_package(mysvac-jsonlib CONFIG REQUIRED)
target_link_libraries(main PRIVATE mysvac::jsonlib)
// C++代码 导入头文件
#include "mysvac/jsonlib.h"
// 导入命名空间简化代码,可选
using namespace Jsonlib;
1. 基本类型介绍
三种可操作类型和六种JSON数据类型:
// 广义的“值类型”,包含 对象 数组 等全部6种JSON数据类型。
class JsonValue;
// 对象类型,本质是 std::map<std::string, JsonValue>
class JsonObject;
// 数组类型,本质是 std::vector<JsonValue>
class JsonArray;
// 使用 JsonValue.type() 函数,获取内部JSON数据类型
enum class JsonType{
OBJECT, /**< JSON 对象类型 */
ARRAY,/**< JSON 数组类型 */
STRING, /**< JSON 字符串类型 */
NUMBER, /**< JSON 数值类型 */
BOOL, /**< JSON 布尔类型 */
ISNULL, /**< JSON null类型 */
};
2. 列表初始化
需要注意,既然存在列表初始化函数,就要谨慎使用{ }
运算符进行初始化。
因为{ }
和( )
初始化器会带来不同的行为。
参考代码:
JsonValue json1 = {
{ "key", nullptr },
{ 1, 2, 3, 4, 5 },
"string",
true,
false,
1234.5
};
生成规则:
- 如果初始化器为空,则生成ISNULL类型对象。
- 如果初始化器只有2个元素,且第1个元素是字符串,则生成OBJECT。
- 其他情况则生成ARRAY类型对象。
注意规则3,{}
运算符构造,内部只有1个元素,依然只会生成ARRAY类型对象。
而()
运算符构造,等同赋值语句,根据参数类型生成不同类型的对象。
注意规则2,有的时候需要ARRAY对象,却会被此规则生成OBJECT对象。
所以我更推荐你在类型模糊时,使用如下方式创建:
// JsonObject本质是std::map<std::string, JsonValue>,必须套双重括号进行列表初始化
JsonValue json1 = JsonArray{
JsonObject { {"key", nullptr} },
JsonArray{ 1, 2, 3, 4, 5 },
"string",
true,
false,
1234.5
};
3. 反序列化与序列化
使用deserialize()
函数进行反序列化。
使用对象.serialize()
成员函数进行序列化。
使用对象.serialize_pretty()
函数函数进行美化序列化,可指定缩进长度。
参考代码:
JsonValue json = deserialize(R"(
{
"语法": ["C++", "原始字符串", false ],
"key": "支持\t中文\\\n与\"转义字符",
"na\"\\me": [ 114,514 , null ],
"map": [ {} , [ [ "嵌套" ] , {} ] ]
}
)");
// serialize序列化 不保留无效空格
std::cout << json.serialize() << std::endl;
// serialize_pretty序列化 带空格和换行,默认一次缩进2空格,可指定
std::cout << json.serialize_pretty() << std::endl;
4. 增删改查
需要注意的是,数组类型本质是std::vector
,所以中间插入和删除元素是O(m)的,修改是正常O(1)。
无特殊情况,尽量使用push_back()
和pop_back()
在末尾修改。
而对象类型的本质是std::map
,操作都是O(log m)级别。
参考代码:
// json变量是上面【3. 反序列...】中的json变量
json.erase("na\"\\me"); // 删除
json["map"][1].clear(); // 清空
json["语法"] = 114514; // 修改
json["add"] = deserialize("[[[]]]"); //增加
json["add"].push_back(1);
std::cout << json.serialize() << std::endl;
std::cout << json["key"].as_string() << std::endl; // 获取字符串并转义
可能的输出:
{"add":[[[]],1],"key":"支持\t中文\\\n与\"转义字符","map":[{},[]],"语法":114514}
支持 中文\
与"转义字符
5.使用is检测类型,使用as获取内容
参考代码:
JsonValue value = 123456789012345ll;
// is保证不会抛出异常
value.is_array(); // false
value.is_object(); // false
value.is_double(); // false 内部没有小数点
value.is_number(); // true int64和double都算number
// as 转换失败时抛出异常
value.as_int64(); // 123456789012345ll
value.as_double(); // 1.23457e+14 能够转化,但可能丢失精度
value.as_array(); // 抛出异常 Jsonlib::JsonTypeException
6. 迭代器与移动语义支持
需要注意的是,JsonValue类型不支持迭代器,因为内部类型不确定。
但是可以通过as_array()
和as_object()
获取内部元素的引用,然后使用迭代器。
因为JsonArray本质是std::vector<JsonArray>
,而JsonObject是std::map<std::string, JsonValue>
。
移动语义当然是完全支持的,且JsonArray和JsonObject对象可以赋值/移动给JsonValue,必然成功,不会抛出异常。
参考代码:
JsonValue my_obj = { "key1", { nullptr, 666 } };
JsonValue my_arr = JsonArray { true, JsonObject{} };
// 字符串构造,不会解析内部数据,不会报错,注意使用()而不是{}
JsonValue my_val ("[ {} this is string ]");
// as_array()和as_object()返回引用,其他的as返回副本
for(auto& it: my_arr.as_array()){
// it 的类型是 Jsonlib::JsonValue&
// 具体操作...
}
// 支持移动,被移动的对象变成JsonType::ISNULL类型,不会删除
my_arr.insert(1, std::move(my_obj["key"]));
my_arr.push_back(my_val);
std::cout << my_arr.serialize_pretty() << std::endl;
可能的输出:
[
true,
[
null,
666
],
{ },
"[ {} this is string ]"
]
提醒:
不推荐的写法:
// 标准库容器不保证被移动后变回初始状态。
B = std::move(A.as_object()); // ❌ 总是可行,但是不推荐这样写。
推荐的写法:
// 本库的JsonValue类型,保证被移动后重置为ISNULL状态,可以正常使用。
B = std::move(A); // ✅ A会被重置为初始状态。
B = std::move(A["xxx"]); // ✅ 这样访问子元素,得到的类型是JsonValue&,所以也是安全的。
7. 自定义类型的序列化
你可以通过重载类型转换运算符,实现自定义类型的JSON格式化。
参考代码:
struct A{
std::string name;
int value;
bool check;
// 自行判断是否添加 explicit
operator JsonValue() const {
JsonValue result(JsonType::OBJECT);
result["name"] = name;
result["value"] = value;
result["check"] = check;
return result;
};
};
需要的的时候,通过类型转换进行序列化,甚至直接赋值:
A a {"XX", 1, true};
std::cout << JsonValue(a).serialize(); // ✅
JsonValue json = a; // ✅ 如果声明了explicit,或许要显示转换
或者尝试写一个宏:
#define Field(name) result[#name] = name;
#define Serializable(...) \
operator Jsonlib::JsonValue(){ \
Jsonlib::JsonValue result(Jsonlib::JsonType::OBJECT); \
__VA_ARGS__ \
return result; \
}
然后可以通过宏进行注册,实现一样的效果:
struct A{
std::string name;
int value;
bool check;
Serializable(
Field(name)
Field(value)
Field(check)
);
};
这样的宏还可以实现类型的嵌套,只要成员变量能隐式转换成JsonValue类型。
库头文件中并没有提供这样的宏,作者认为宏的导入会污染代码。
如果需要,你可以将上述宏函数代码赋值到自己的项目中使用。
8. 异常处理
本库使用了三种自定义异常和一种标准:
JsonException
: 继承自std::runtime_runtime_error
,没有地方抛出此异常。JsonTypeException
: 继承自JsonException
,表示类型错误,比如as_xxx()
函数。JsonStructureException
: 继承自JsonException
,表示JSON结构错误,导致反序列化失败。std::out_of_range
: 使用at()
严格访问子元素,元素不存在或越界时抛出,
参考代码:
try{
JsonValue json = deserialize("[ {}} ]");
}
catch(const JsonStructureException& e){
std::cerr << "JsonStructureException: " << e.what() << std::endl;
}
catch(const JsonException& e){
std::cerr << "JsonException: " << e.what() << std::endl;
}
catch(...){ std::cerr << "other" << std::endl; }
可能的输出:
JsonStructureException: Unknown Json Structure.
注意
赋值/拷贝/移动/序列化/is
/type
/size
…等操作保证不会抛出异常。
只有deserialze()
反序列化函数,或者as
类型转换失败,访问越界时可能抛出异常。
性能概述
时间复杂度其实没什么用,看看就好,后面都是常数优化。
- N : JSON文本长度。
- m : 子元素个数。
- 下面提供最坏情况的时空复杂度(虽然没什么用,后面都是常数优化。):
- 序列化:时间复杂度O(N),空间复杂度O(N)。
- 反序列化: 时间复杂度O(N),空间复杂度O(N)。
- 键值对-增删改查: O(log m)。
- 数组-增删: 末尾操作O(1),其余位置平均O(m)。
- 数组-改查: O(1)。
性能对比测试参考:https://github.com/Mysvac/cpp-json-test