当前位置: 首页 > news >正文

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::istreamstd::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_jsonfrom_json 必须要位于要适配的自定义类的命名空间,不然库找不到,示例中都放到了 ns 中
  • 在方法中,使用at(),而不是.,如果键不在,会引发异常以便处理
  • 必须都为内置类型,自定义类型还需要实现上述两个方法

限制

  • 只支持UTF-8作为输入
  • 不支持json注释
  • 默认不保证 json 中 key 的顺序,即打印的key的顺序是随机的。如果要支持,使用 nlohmann::ordered_json 试试。

以上就是本篇博客的所有内容,感谢你的阅读
如果觉得本篇文章对你有所帮助的话,不妨点个赞支持一下博主,拜托啦,这对我真的很重要。
在这里插入图片描述

相关文章:

  • CefSharp浏览器(AntdUI.Tabs)标签页关闭时资源释放ChromiumWebBrowser示例源码
  • 【文献笔记】LLM-based and retrieval-augmented control code generation
  • SmolVLM新模型技术解读笔记
  • 联邦学习与协作学习:数据隐私与模型协同进化的未来之路
  • 在SpringBoot中访问 static 与 templates 目录下的内容
  • 在 MySQL 单表存储 500 万数据的场景下,如何设计读取
  • 冲刺高分!挑战7天一篇nhanes机器学习SCI!DAY1-7
  • 1023 Have Fun with Numbers
  • Python基础语法——常量变量
  • 【Linux】进程的程序替换、自定义shell命令行解释器
  • 批量将多个文件按扩展名分类到不同文件夹
  • 如何实现动态请求地址(baseURL)
  • 数据库案例1--视图和索引
  • lvs + keepalived + dns 高可用
  • 嵌入式开发
  • 实时数据同步方案
  • 网络安全·第四天·扫描工具Nmap的运用
  • libaom 码率控制实验:从理论到实践的深度探索
  • 水污染治理(生物膜+机器学习)
  • Android离屏渲染
  • 央媒关注微短剧如何探索精品化之路:从“悬浮”落回“现实”
  • 当瓷器传入欧洲,看女性视角下的中国风
  • 钧正平发声:擅自更改地图标注,谷歌想当南海搅局者?!
  • 上传150个电视剧切条短视频到网上,博主被判赔2万元
  • 东南亚三国行第三日|中马将在人工智能、大熊猫保护、铁路等多领域深化合作
  • 深圳机器人“十三太保”亮相上海,所为何事?