C++第三方库【JSON】nlohman/json
文章目录
- 优势
- 使用
- API
- 从文件中读取json
- 从json文本创建json对象
- 直接创建并操作json对象
- 字符串 <=> json对象
- 文件流 <=> json对象
- 从迭代器读取
- 像使用STL一样的访问
- STL容器转化为 json数组
- STL容器 转 json对象
- 自定义类型转化为 json对象
- 限制
优势
- 直观的语法:json的使用像是使用一个STL容器
- 简单的引入:仅需要包含一个json.hpp头文件,不需要库,没有子项目,没有依赖项,没有复杂的构建系统。使用原版C++11编写。加快开发速度
- 内存效率:经过严格的单元测试,没有内存泄露;每个JSON对象的开销为一个指针和一个枚举元素(1个字节)。默认泛化使用以下C++数据类型:字符串、数字、对象、数组、布尔值。但也可以根据需要对
Generalized类
进行模板化(std::string、int64_t、uint64_t、double、std::map、std::vectort、bool、basic_json)
使用
直接包含
dir/
└── nlohmann├── json_fwd.hpp└── json.hpp
└── a.cpp
一般只要包含json.hpp,如果需要forward-declarations
,那么可以包含json_fwd.hpp
注意,因为库使用了C++11,所以编译的时候需要使用C++11以上的标准编译
#include <iostream>
#include "nlohmann/json.hpp"using json = nlohmann::json;int main()
{// 使用不同类型的默认值创建JSON值json j_null(json::value_t::null);json j_boolean(json::value_t::boolean);json j_number_integer(json::value_t::number_integer);json j_number_float(json::value_t::number_float);json j_object(json::value_t::object);json j_array(json::value_t::array);json j_string(json::value_t::string);// 序列化JSON值std::cout << j_null << '\n';std::cout << j_boolean << '\n';std::cout << j_number_integer << '\n';std::cout << j_number_float << '\n';std::cout << j_object << '\n';std::cout << j_array << '\n';std::cout << j_string << '\n';
}
运行结果:
$./a.out
null
false
0
0.0
{}
[]
""
API
从文件中读取json
自动解析文件中的JSON,创建一个json对象
#include <fstream>
#include <nlohmann/json.hpp>
using json = nlohmann::json;std::ifstream f("example.json");
json data = json::parse(f);
从json文本创建json对象
假设要通过下述的json文本创建json对象
{"pi": 3.141,"happy": true
}
方法:
// 使用原始字符串
json ex1 = json::parse(R"({"pi": 3.141,"happy": true}
)");// 直接赋值字符串,需要带上_json !!!
using namespace nlohmann::literals;
json ex2 = R"({"pi": 3.141,"happy": true}
)"_json;// 使用列表初始化
json ex3 = {{"happy", true},{"pi", 3.141},
};
直接创建并操作json对象
假设要通过下述json文本创建json对象
注意:不能带注释,对象/数组的最后一个元素后不要有,(逗号)
{"pi": 3.141,"happy": true,"name": "Niels","nothing": null,"answer": {"everything": 42},"list": [1, 0, 2],"object": {"currency": "USD","value": 42.99}
}
构建方法:
// 创建一个空的结构体
json j;// 添加一个存储为double的数字(注意j到object的转换)
j["pi"] = 3.141;// 添加一个bool值,存储为bool
j["happy"] = true;// 添加一个字符串,存储为std::string
j["name"] = "Niels";// 添加null对象
j["nothing"] = nullptr;// 添加嵌套对象
j["answer"]["everything"] = 42;// 添加一个数组,存储为std::vector(列表初始化)
j["list"] = { 1, 0, 2 };// 添加另一个对象(使用列表初始化)
j["object"] = { {"currency", "USD"}, {"value", 42.99} };// 直接通过列表创建
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对象
从字符串反序列化到 json 对象,需要使用_json
// create object from string literal
json j = "{ \"happy\": true, \"pi\": 3.141 }"_json;// or even nicer with a raw string literal
auto j2 = R"({"happy": true,"pi": 3.141}
)"_json;
如果不加 _json
,则不会解析传递的字符串文本,而只是用作 json 字符串。
使用 _json
需要先展开命名空间
using namespace nlohman::literals
上述例子同样可以使用 json::parse()
转化
auto j3 = json::parse(R"({"happy": true, "pi": 3.141})");
json对象 => 字符串
std::string s = j.dump(); // {"happy":true,"pi":3.141}// 序列化到好看的格式
// 传入要缩进的空格数
std::cout << j.dump(4) << std::endl;
// {
// "happy": true,
// "pi": 3.141
// }
注意序列化和赋值的区别
// 存储一个字符串
json j_string = "this is a string";// 转化为字符串导出
auto cpp_string = j_string.template get<std::string>();
// 适配变量的类型导出(当变量已存在时为 alternative)
std::string cpp_string2;
j_string.get_to(cpp_string2);// 序列化
std::string serialized_string = j_string.dump();// 这三个字符串是相等的
std::cout << cpp_string << " == " << cpp_string2 << " == " << j_string.template get<std::string>() << '\n';
// 这两个字符串是相等的
std::cout << j_string << " == " << serialized_string << std::endl;
该库仅支持 UTF-8。当在库中存储不同编码的字符串时,调用dump可能会引发异常json::error_handler_t::replacejson::error_handler_t::ignore
文件流 <=> json对象
还可以使用 streams 进行序列化和反序列化:
// 从标准输入反序列化
json j;
std::cin >> j;// 通过标准输出序列化
std::cout << j;// 格式控制,重载了setw
std::cout << std::setw(4) << j << std::endl;
也适用于 stream 的子类。比如std::istream
、std::ostream
// 读一个json文件
std::ifstream i("file.json");
json j;
i >> j;// 将美化的json写到另一个文件
std::ofstream o("pretty.json");
o << std::setw(4) << j << std::endl;
从迭代器读取
还可以从迭代器范围解析 JSON
从迭代器可访问的任何容器中,其整数类型为 1、2 或 4 字节,将分别解释为 UTF-8、UTF-16 和 UTF-32。例如uint8_t、uint16_t
std::vector<std::uint8_t> v = {'t', 'r', 'u', 'e'};
json j = json::parse(v.begin(), v.end());
像使用STL一样的访问
// 通过push_back创建数组
json j;
j.push_back("foo");
j.push_back(1);
j.push_back(true);// 也可以emplace_back
j.emplace_back(1.78);// 通过迭代器访问json数组
for (json::iterator it = j.begin(); it != j.end(); ++it) {std::cout << *it << '\n';
}// 范围for
for (auto& element : j) {std::cout << element << '\n';
}// 访问和设置
const auto tmp = j[0].template get<std::string>();
j[1] = 42;
bool foo = j.at(2);// 比较
j == R"(["foo", 1, true, 1.78])"_json; // true// 其他方法
j.size(); // 4 entries
j.empty(); // false
j.type(); // json::value_t::array
j.clear(); // the array is empty again// 类型检查
j.is_null();
j.is_boolean();
j.is_number();
j.is_object();
j.is_array();
j.is_string();// 创建对象
json o;
o["foo"] = 23;
o["bar"] = false;
o["baz"] = 3.141;// 也可以使用emplace
o.emplace("weather", "sunny");// 对象迭代器
for (json::iterator it = o.begin(); it != o.end(); ++it) {std::cout << it.key() << " : " << it.value() << "\n";
}// 范围for
for (auto& el : o.items()) {std::cout << el.key() << " : " << el.value() << "\n";
}// 结构化绑定
for (auto& [key, value] : o.items()) {std::cout << key << " : " << value << "\n";
}// 查找元素
if (o.contains("foo")) {// there is an entry with key "foo"
}// 查找到结尾
if (o.find("foo") != o.end()) {// there is an entry with key "foo"
}// 返回个数
int foo_present = o.count("foo"); // 1
int fob_present = o.count("fob"); // 0// 删除元素
o.erase("foo");
STL容器转化为 json数组
std::vector<int> c_vector{1, 2, 3, 4};json j_vec(c_vector);std::cout << j_vec << '\n';std::deque<double> c_deque{1.2, 2.3, 3.4, 5.6};json j_deque(c_deque);std::cout << j_deque << '\n';std::list<bool> c_list{true, true, false, true};json j_list(c_list);std::cout << j_list << '\n';std::forward_list<int64_t> c_flist{12345678909876, 23456789098765,34567890987654, 45678909876543};json j_flist(c_flist);std::cout << j_list << '\n';std::array<unsigned long, 4> c_array{{1, 2, 3, 4}};json j_array(c_array);std::cout << j_array << '\n';std::set<std::string> c_set{"one", "two", "three", "four", "one"};json j_set(c_set); // 只有一个"one"std::cout << j_set << '\n';std::unordered_set<std::string> c_uset{"one", "two", "three", "four", "one"};json j_uset(c_uset); // 只有一个"one"std::cout << j_uset << '\n';std::multiset<std::string> c_mset{"one", "two", "one", "four"};json j_mset(c_mset); // 有多个"one"std::cout << j_mset << '\n';std::unordered_multiset<std::string> c_umset{"one", "two", "one", "four"};json j_umset(c_umset); // 有多个"one"std::cout << j_umset << '\n';
STL容器 转 json对象
std::map<std::string, int> c_map{{"one", 1}, {"two", 2}, {"three", 3}};json j_map(c_map);std::cout << j_map << '\n';std::unordered_map<const char *, double> c_umap{{"one", 1.2}, {"two", 2.3}, {"three", 3.4}};json j_umap(c_umap);std::cout << j_umap << '\n';std::multimap<std::string, bool> c_mmap{{"one", true}, {"two", true}, {"three", false}, {"three", true}};json j_mmap(c_mmap); // 只有一个"three"对象std::cout << j_mmap << '\n';std::unordered_multimap<std::string, bool> c_ummap{{"one", true}, {"two", true}, {"three", false}, {"three", true}};json j_ummap(c_ummap); // 只有一个"three"对象std::cout << j_ummap << '\n';
自定义类型转化为 json对象
假设有如下类
namespace ns{struct person {std::string name;std::string address;int age;};
}
实现如下两个方法,即可支持 person 直接转化为 json 对象
using json = nlohmann::json;namespace ns{void to_json(json& j, const person& p) {j = json{{"name", p.name}, {"address", p.address}, {"age", p.age}};}void from_json(const json& j, person& p) {j.at("name").get_to(p.name);j.at("address").get_to(p.address);j.at("age").get_to(p.age);}
}
即可
// 创建一个person对象
ns::person p {"Ned Flanders", "744 Evergreen Terrace", 60};// 将 person 对象转化为 json 对象
json j = p;std::cout << j << std::endl;
// {"address":"744 Evergreen Terrace","age":60,"name":"Ned Flanders"}// 将 json 对象转化为 person 对象
auto p2 = j.template get<ns::person>();// 相等的
assert(p == p2);
注意:
to_json
和from_json
必须要位于要适配的自定义类的命名空间,不然库找不到,示例中都放到了 ns 中- 在方法中,使用
at()
,而不是.
,如果键不在,会引发异常以便处理 - 必须都为内置类型,自定义类型还需要实现上述两个方法
限制
- 只支持UTF-8作为输入
- 不支持json注释
- 默认不保证 json 中 key 的顺序,即打印的key的顺序是随机的。如果要支持,使用
nlohmann::ordered_json
试试。
以上就是本篇博客的所有内容,感谢你的阅读
如果觉得本篇文章对你有所帮助的话,不妨点个赞支持一下博主,拜托啦,这对我真的很重要。