nlohmann-json库使用简介
GitHub地址 nlohmann/json
简介
JSON库千千万,为何nlohmann/json
库那么热门呢?原因在于它的设计理念非常人性化:
- 提供符合直觉的语法:可以像STL容器一样来使用,提供了丰富而且符合直觉的接口;
- 简易的导入方式:仅头文件(header-only)形式的JSON库,方便整合到项目中;
- 严谨的测试:简而言之就是不用担心库的解析出问题,准确率贼高。
缺点也是有的:
因此,在不需要高性能的场景下,使用这个库来解析和生成JSON文件是很方便的。如果需要高性能,还是老老实实用rapidjson吧。
基本用例
先从官方的简易用例开始
生成JSON结构的数据
若需要生成如下结构的JSON数据
{
"pi": 3.141,
"happy": true,
"name": "Niels",
"nothing": null,
"answer": {
"everything": 42
},
"list": [1, 0, 2],
"object": {
"currency": "USD",
"value": 42.99
}
}
只需要这样编写代码:
#include <nlohmann/json.hpp>
using namspace nlohmann;
// 构造一个json对象(null)
json j;
// 添加一个json对象,key为pi,值为double类型的3.141
j["pi"] = 3.141;
// 以 bool 形式存储一个布尔值
j["happy"] = true;
// 以 std::string 的形式存储一个字符串对象
j["name"] = "Niels";
// 通过nullptr向key为nothing的对象中添加一个json对象
j["nothing"] = nullptr;
// 在一个json对象中添加一个json对象
j["answer"]["everything"] = 42;
// 以 std::vector 形式保存一个数组(使用std::initializer_list)
j["list"] = { 1, 0, 2 };
// 添加另一个json对象(以pair的形式)
j["object"] = { {"currency", "USD"}, {"value", 42.99} };
通过gdb调试来看json对象的变化
甚至可以用大括号初始化的方式来一步到位初始化JSON数据结构;
json j2 = {
{"pi", 3.141},
{"happy", true},
{"name", "Niels"},
{"nothing", nullptr},
{"answer", {
{"everything", 42}
}},
{"list", {1, 0, 2}},
{"object", {
{"currency", "USD"},
{"value", 42.99}
}}
};
输入输出JSON文件
输入输出JSON文件可以简单地用std::fstream
来做
std::fstream ofs("output.json", std::ios::out);
ofs << j.dump();
#include <nlohmann/json.hpp>
#include <fstream>
#include <iostream>
using namespace nlohmann;
int main(int argc, char** argv)
{
if (std::fstream ifs("output.json", std::ios::in); ifs)
{
json j = json::parse(ifs);
for (auto const& item : j.items())
{
std::cout << item << '\n';
}
}
return 0;
}
从STL容器转换到JSON
这里就直接贴官方样例了,很好理解
std::vector<int> c_vector {1, 2, 3, 4};
json j_vec(c_vector);
// [1, 2, 3, 4]
std::deque<double> c_deque {1.2, 2.3, 3.4, 5.6};
json j_deque(c_deque);
// [1.2, 2.3, 3.4, 5.6]
std::list<bool> c_list {true, true, false, true};
json j_list(c_list);
// [true, true, false, true]
std::forward_list<int64_t> c_flist {12345678909876, 23456789098765, 34567890987654, 45678909876543};
json j_flist(c_flist);
// [12345678909876, 23456789098765, 34567890987654, 45678909876543]
std::array<unsigned long, 4> c_array {{1, 2, 3, 4}};
json j_array(c_array);
// [1, 2, 3, 4]
std::set<std::string> c_set {"one", "two", "three", "four", "one"};
json j_set(c_set); // only one entry for "one" is used
// ["four", "one", "three", "two"]
std::unordered_set<std::string> c_uset {"one", "two", "three", "four", "one"};
json j_uset(c_uset); // only one entry for "one" is used
// maybe ["two", "three", "four", "one"]
std::multiset<std::string> c_mset {"one", "two", "one", "four"};
json j_mset(c_mset); // both entries for "one" are used
// maybe ["one", "two", "one", "four"]
std::unordered_multiset<std::string> c_umset {"one", "two", "one", "four"};
json j_umset(c_umset); // both entries for "one" are used
// maybe ["one", "two", "one", "four"]
std::map<std::string, int> c_map { {"one", 1}, {"two", 2}, {"three", 3} };
json j_map(c_map);
// {"one": 1, "three": 3, "two": 2 }
std::unordered_map<const char*, double> c_umap { {"one", 1.2}, {"two", 2.3}, {"three", 3.4} };
json j_umap(c_umap);
// {"one": 1.2, "two": 2.3, "three": 3.4}
std::multimap<std::string, bool> c_mmap { {"one", true}, {"two", true}, {"three", false}, {"three", true} };
json j_mmap(c_mmap); // only one entry for key "three" is used
// maybe {"one": true, "two": true, "three": true}
std::unordered_multimap<std::string, bool> c_ummap { {"one", true}, {"two", true}, {"three", false}, {"three", true} };
json j_ummap(c_ummap); // only one entry for key "three" is used
// maybe {"one": true, "two": true, "three": true}
自定义结构体转换函数(序列化/反序列化)
假设我们现在有一个这样的结构需要序列化为JSON格式的文本:
#include <fstream>
#include <nlohmann/json.hpp>
#include <string>
#include <vector>
using namespace nlohmann;
using nlohmann::json;
namespace wb
{
struct Asset
{
std::string name;
int value;
};
struct Person
{
std::string name;
int age;
bool married;
std::vector<std::string> habbits;
std::vector<Asset> assets;
};
void to_json(json &j, const Asset &a)
{
j = json{{"name", a.name}, {"value", a.value}};
}
void to_json(json &j, const Person &p)
{
j = json{{"name", p.name}, {"age", p.age}, {"married", p.married}};
j["habbits"] = p.habbits;
for (auto const &a : p.assets)
{
json ja;
to_json(ja, a);
j["assets"].emplace_back(std::move(ja));
}
}
} // namespace wb
针对结构体中的每个结构都需要写一个to_json
函数用于转换(这个函数的名字可以任取,不一定是to_json
)。
需要注意的是,对于每个自定义的结构,都需要自行提供to_json
函数的实现。
使用以下代码进行测试:
int main(int argc, char **argv)
{
wb::Person wb = {"wb",
18,
true,
{"football", "reading"},
{{"house", 9999}, {"car", 8888}}};
if (std::fstream ofs("output.json", std::ios::out); ofs)
{
json j;
wb::to_json(j, wb);
ofs << j.dump(4) << '\n'; // 缩进为4的JSON格式
}
return 0;
}
可以得到输出的output.json文件如下:
{
"age": 18,
"assets": [
{
"name": "house",
"value": 9999
},
{
"name": "car",
"value": 8888
}
],
"habbits": [
"football",
"reading"
],
"married": true,
"name": "wb"
}
读取JSON文件,反序列化的例子如下:
// 头文件和结构体定义略
void from_json(const json& j, Asset& a)
{
j.at("name").get_to(a.name);
j.at("value").get_to(a.value);
}
void from_json(const json& j, Person& p)
{
j.at("name").get_to(p.name);
j.at("age").get_to(p.age);
j.at("married").get_to(p.married);
j.at("habbits").get_to(p.habbits);
for (auto const& ja : j.at("assets"))
{
Asset a;
from_json(ja, a);
p.assets.emplace_back(std::move(a));
}
}
// 为了方便验证结果,加入了一个naive的运算符重载
bool operator==(const Person& lhs, const Person& rhs)
{
return lhs.name == rhs.name;
}
} // namespace wb
在完成了from_json
函数后(同样不一定需要命名为from_json
,可以是其他名字),就可以轻易地对JSON格式的文本文件反序列化从而得到一个结构体了。
使用下面的代码进行测试:
int main(int argc, char** argv)
{
wb::Person wb = {"wb",
18,
true,
{"football", "reading"},
{{"house", 9999}, {"car", 8888}}};
if (std::fstream ifs("output.json", std::ios::in); ifs)
{
json j = json::parse(ifs);
wb::Person pj;
from_json(j, pj);
assert(wb == pj);
std::cout << "same" << std::endl;
}
return 0;
}
运行结果打印"same",说明pj
和wb
两个结构体是"相等"的(只比较了name
字段,这显然这不严谨,但是为了演示简便就省略了过多的代码)。
总结
至此,我们就掌握了nlohmann/json
库的常用方法了。其实还有很多特殊的技巧,但是不一定常用,有兴趣的读者可以自己探索。
顺便提一下CMake项目中引用nlohmann/json
库的方法:
find_package(nlohmann_json 3.2.0 REQUIRED)
add_library(foo ...)
target_link_libraries(foo PRIVATE nlohmann_json::nlohmann_json)