【3rd Party】nlohmann json 基础用法

参考链接:Here

什么是nlohman json ?

nlohman json GitHub - nlohmann/json: JSON for Modern C++ 是一个为现代C++(C++11)设计的JSON解析库,主要特点是

  1. 易于集成,仅需一个头文件,无需安装依赖
  2. 易于使用,可以和STL无缝对接,使用体验近似python中的json

Minimal Example

CMakeLists.txt 编写教程:Here

  1. # CMakeLists.txt
  2. cmake_minimum_required(VERSION 3.24)
  3. project(nlohmannJson)
  4. set(CMAKE_CXX_STANDARD 17)
  5. include_directories("include")
  6. add_executable(${PROJECT_NAME} src/main.cpp)
  1. // main.cpp
  2. #include <iostream>
  3. #include "nlohmann/json.hpp"
  4. using namespace std;
  5. using namespace nlohmann;
  6. int main() {
  7. auto config_json = json::parse(R"({"Happy": true, "pi": 3.1415})");
  8. cout << config_json << endl;
  9. return 0;
  10. }

Advanced Sample

示范用法

nlohman::json 库操作的基本对象是 json Object,全部操作围绕此展开

引入代码

  1. #include <fstream>
  2. #include <nlohmann/json.hpp>
  3. using json = nlohmann::json;

读取 JSON 文件

  1. #include <fstream>
  2. #include <nlohmann/json.hpp>
  3. using json = nlohmann::json;
  4. // ...
  5. // std::ifstream f("example.json");
  6. std::ifstream f("../config/example.json")
  7. json data = json::parse(f);

从JSON文本创建JSON对象

假设您要在文件中创建此文本 JSON 值作为对象:

  1. {
  2. "pi": 3.141,
  3. "happy": true
  4. }

有多种选择:

  1. // 用原始字符串和json::parse进行初始化
  2. json ex1 = json::parse(R"(
  3. {
  4. "pi": 3.1415,
  5. "happy": true
  6. }
  7. )");
  8. // 用原始字符串和literals进行初始化
  9. using namespace nlohmann::literals;
  10. json ex2 = R"(
  11. {
  12. "pi": 3.141,
  13. "happy": true
  14. }
  15. )"_json;
  16. // 使用初始化列表
  17. json ex3 = {
  18. {"happy", true},
  19. {"pi", 3.141},
  20. };

JSON 作为第一类数据类型

下面是一些示例,可让您了解如何使用该类。

假设您要创建 JSON 对象

  1. {
  2. "pi": 3.141,
  3. "happy": true,
  4. "name": "Koshkaaa",
  5. "nothing": null,
  6. "answer": {
  7. "everything": 42
  8. },
  9. "list": [1, 0, 2],
  10. "object": {
  11. "currency": "USD",
  12. "value": 42.99
  13. }
  14. }

使用此库,您可以编写:

  1. // 创建一个空 json 对象 (null)
  2. json j;
  3. // 添加一个 double 类型成员
  4. j["pi"] = 3.141;
  5. // 添加一个 bool 类型的成员
  6. j["happy"] = true;
  7. // 添加一个字符串类型的成员
  8. j["name"] = "Koshkaaa";
  9. // 添加一个空成员
  10. j["nothing"] = nullptr;
  11. // 添加一个对象中的对象成员 {"answer":{"everything":42}}
  12. j["answer"]["everything"] = 42;
  13. // 添加一个数组对象, 使用 vector
  14. j["list"] = {1,0,2};
  15. // 添加一个对象,使用初始化列表 {"object":{"currency": "USD","value":42.99}}
  16. j["object"] = { {"currency", "USD"}, {"value", 42.99} };
  17. // 也可以一次性写完
  18. json j2 = {
  19. {"pi", 3.141},
  20. {"happy", true},
  21. {"name", "Koshkaaa"},
  22. {"nothing", nullptr},
  23. {"answer", {
  24. {"everything", 42}
  25. }},
  26. {"list", {1, 0, 2}},
  27. {"object", {
  28. {"currency", "USD"},
  29. {"value", 42.99}
  30. }}
  31. };

请注意,在所有这些情况下,您永远不需要“告诉”编译器要使用哪种 JSON 值类型。如果你想明确或表达一些边缘情况,函数json::array()和json::object()会有所帮助:

  1. // 初始化一个空数组对象
  2. json empty_array_explicit = json::array();
  3. // 初始化一个对象
  4. json empty_object_implicit = json({});
  5. json empty_object_explicit = json::object();
  6. // 初始化数组键值对 [["currency", "USD"], ["value", 42.99]]
  7. json array_not_object = json::array({ {"currency", "USD"}, {"value", 42.99} });

序列化/反序列化

json对象和string互转JSON对象和string互转

您可以通过追加到字符串文本来创建 JSON 值(反序列化):_json

  1. // 从字符串创建json对象
  2. json j = "{ \"happy\": true, \"pi\": 3.141 }"_json;
  3. // 使用原始字符串创建json对象
  4. auto j2 = R"(
  5. {
  6. "happy": true,
  7. "pi": 3.141
  8. }
  9. )"_json;

请注意,如果不附加后缀,则传递的字符串文字不会被解析,而只是用作 JSON 字符串 价值。也就是说,只会存储字符串而不是解析实际对象。_jsonjson j = "{ \"happy\": true, \"pi\": 3.141 }""{ "happy": true, "pi": 3.141 }"

上面的例子也可以用json::parse()显式表示:

auto j3 = json::parse(R"({"happy": true, "pi": 3.141})");

获取 JSON 字符串(序列化):

  1. std::string s = j.dump(); // {"happy":true,"pi":3.141}
  2. // 序列化美化json形式
  3. // 传入数字指定缩进空格数
  4. std::cout << j.dump(4) << std::endl;
  5. // {
  6. // "happy": true,
  7. // "pi": 3.141
  8. // }

流输入输出(例如文件读写、字符串流)

您还可以使用流来序列化和反序列化:

  1. // 从标准输入中反序列化
  2. json j;
  3. std::cin >> j;
  4. // 序列化到标准输出
  5. std::cout << j;
  6. // 美化输出
  7. std::cout << std::setw(4) << j << std::endl;

文件读写json:std::istream,std::ostream

  1. // 读取json文件
  2. std::ifstream i("file.json");
  3. json j;
  4. i >> j;
  5. //json对象美化输出到文件
  6. std::ofstream o("pretty.json");
  7. o << std::setw(4) << j << std::endl;

从迭代器范围读取json

可以从迭代器范围解析 JSON;也就是说,从迭代器可访问的任何容器中,迭代器是 1、2 或 4 个字节的整数类型,将分别解释为 UTF-8、UTF-16 和 UTF-32。例如,std::vector<std::uint8_t>,std::list<std::uint16_t>

  1. std::vector<std::uint8_t> v = {'t', 'r', 'u', 'e'};
  2. json j = json::parse(v.begin(), v.end());
  1. std::vector<std::uint8_t> v = {'t', 'r', 'u', 'e'};
  2. json j = json::parse(v);

自定义数据源

由于 parse 函数接受任意迭代器范围,因此您可以通过实现概念来提供自己的数据源。LegacyInputIterator

  1. struct MyContainer {
  2. void advance();
  3. const char& get_current();
  4. };
  5. struct MyIterator {
  6. using difference_type = std::ptrdiff_t;
  7. using value_type = char;
  8. using pointer = const char*;
  9. using reference = const char&;
  10. using iterator_category = std::input_iterator_tag;
  11. MyIterator& operator++() {
  12. MyContainer.advance();
  13. return *this;
  14. }
  15. bool operator!=(const MyIterator& rhs) const {
  16. return rhs.target != target;
  17. }
  18. reference operator*() const {
  19. return target.get_current();
  20. }
  21. MyContainer* target = nullptr;
  22. };
  23. MyIterator begin(MyContainer& tgt) {
  24. return MyIterator{&tgt};
  25. }
  26. MyIterator end(const MyContainer&) {
  27. return {};
  28. }
  29. void foo() {
  30. MyContainer c;
  31. json j = json::parse(c);
  32. }

类似 STL 的访问

我们将 JSON 类设计为类似于 STL 容器的行为。事实上,它满足可逆容器的要求。

  1. // 用push_back创建一个数组对象
  2. json j;
  3. j.push_back("foo");
  4. j.push_back(1);
  5. j.push_back(true);
  6. // 使用 emplace_back
  7. j.emplace_back(1.78);
  8. // 迭代访问
  9. for (json::iterator it = j.begin(); it != j.end(); ++it) {
  10. std::cout << *it << '\n';
  11. }
  12. // 范围迭代
  13. for (auto& element : j) {
  14. std::cout << element << '\n';
  15. }
  16. // getter/setter接口
  17. const auto tmp = j[0].get<std::string>();
  18. j[1] = 42;
  19. bool foo = j.at(2);
  20. // 比较
  21. j == R"(["foo", 1, true, 1.78])"_json; // true
  22. // 其他
  23. j.size(); // 4 个对象
  24. j.empty(); // 是否为空:false
  25. j.type(); // 获取类型:json::value_t::array
  26. j.clear(); // 清空
  27. // 检查类型
  28. j.is_null();
  29. j.is_boolean();
  30. j.is_number();
  31. j.is_object();
  32. j.is_array();
  33. j.is_string();
  34. // 创建一个对象
  35. json o;
  36. o["foo"] = 23;
  37. o["bar"] = false;
  38. o["baz"] = 3.141;
  39. // 使用 emplace
  40. o.emplace("weather", "sunny");
  41. // 迭代访问
  42. for (json::iterator it = o.begin(); it != o.end(); ++it) {
  43. std::cout << it.key() << " : " << it.value() << "\n";
  44. }
  45. // 循环访问
  46. for (auto& el : o.items()) {
  47. std::cout << el.key() << " : " << el.value() << "\n";
  48. }
  49. // 匿名对象迭代器(C++17)
  50. for (auto& [key, value] : o.items()) {
  51. std::cout << key << " : " << value << "\n";
  52. }
  53. // 查找key
  54. if (o.contains("foo")) {
  55. // 找到foo的键值
  56. }
  57. // 通过迭代器查找
  58. if (o.find("foo") != o.end()) {
  59. // 找到foo的键值
  60. }
  61. // 用count()统计是否有键值
  62. int foo_present = o.count("foo"); // 1
  63. int fob_present = o.count("fob"); // 0
  64. // 删除foo对象
  65. o.erase("foo");

从 STL 容器转换

从STL容器转换到json,std::list转json,std::vector转json

  1. std::vector<int> c_vector {1, 2, 3, 4};
  2. json j_vec(c_vector);
  3. // [1, 2, 3, 4]
  4. std::deque<double> c_deque {1.2, 2.3, 3.4, 5.6};
  5. json j_deque(c_deque);
  6. // [1.2, 2.3, 3.4, 5.6]
  7. std::list<bool> c_list {true, true, false, true};
  8. json j_list(c_list);
  9. // [true, true, false, true]
  10. std::forward_list<int64_t> c_flist {12345678909876, 23456789098765, 34567890987654, 45678909876543};
  11. json j_flist(c_flist);
  12. // [12345678909876, 23456789098765, 34567890987654, 45678909876543]
  13. std::array<unsigned long, 4> c_array {{1, 2, 3, 4}};
  14. json j_array(c_array);
  15. // [1, 2, 3, 4]
  16. std::set<std::string> c_set {"one", "two", "three", "four", "one"};
  17. json j_set(c_set); // only one entry for "one" is used
  18. // ["four", "one", "three", "two"]
  19. std::unordered_set<std::string> c_uset {"one", "two", "three", "four", "one"};
  20. json j_uset(c_uset); // only one entry for "one" is used
  21. // maybe ["two", "three", "four", "one"]
  22. std::multiset<std::string> c_mset {"one", "two", "one", "four"};
  23. json j_mset(c_mset); // both entries for "one" are used
  24. // maybe ["one", "two", "one", "four"]
  25. std::unordered_multiset<std::string> c_umset {"one", "two", "one", "four"};
  26. json j_umset(c_umset); // both entries for "one" are used
  27. // maybe ["one", "two", "one", "four"]

std::mapjsonstd::unorderedjson

  1. std::map<std::string, int> c_map { {"one", 1}, {"two", 2}, {"three", 3} };
  2. json j_map(c_map);
  3. // {"one": 1, "three": 3, "two": 2 }
  4. std::unordered_map<const char*, double> c_umap { {"one", 1.2}, {"two", 2.3}, {"three", 3.4} };
  5. json j_umap(c_umap);
  6. // {"one": 1.2, "two": 2.3, "three": 3.4}
  7. std::multimap<std::string, bool> c_mmap { {"one", true}, {"two", true}, {"three", false}, {"three", true} };
  8. json j_mmap(c_mmap); // only one entry for key "three" is used
  9. // maybe {"one": true, "two": true, "three": true}
  10. std::unordered_multimap<std::string, bool> c_ummap { {"one", true}, {"two", true}, {"three", false}, {"three", true} };
  11. json j_ummap(c_ummap); // only one entry for key "three" is used
  12. // maybe {"one": true, "two": true, "three": true}