当前位置:   article > 正文

【C++ JSON 开源库json.hpp】nlohmann入门使用总结1

json.hpp

一、Json介绍

在网络中,常用的数据传输序列化格式有XML,Json,ProtoBuf

在公司级别的项目中=大量的在使用 ProtoBuf作为数据序列化的方式,但是使用起来比Json稍复杂一些

数据的序列化方式可选择:Json、XML、Protobuf

Json是一种轻量级的数据交换格式(也叫数据序列化方式)。

Json采用完全独立于编程语言的文本格式来存储和表示数据。简洁和清晰的层次结构使得 Json 成为理想的数据交换语言。

易于人阅读和编写,同时也易于机器解析和生成,并有效地提升网络传输效率。

JSON是什么

JSON: JavaScript Object Notation(JavaScript 对象表示法)

JSON 是存储和交换文本信息的语法,类似 XML。

JSON 比 XML 更小、更快,更易解析。

JSON 易于人阅读和编写。

C、Python、C++、Java、PHP、Go等编程语言都支持 JSON。

二、第三方库Json.hpp:nlohman json


JSON for Modern C++ 是一个由德国大牛 nlohmann 编写的在 C++ 下使用的 JSON 库。

开源地址: KingJamesGyq/json

特点:

(1)直观的语法

(2)整个代码由一个头文件组成 json.hpp,没有子项目,没有依赖关系,没有复杂的构建系统,使用起来非常方便

(3)使用 C++ 11 标准编写

(4)使用 json 像使用 STL 容器一样

(5)STL 和 json 容器之间可以相互转换

(6)严谨的测试:所有类都经过严格的单元测试,覆盖了 100% 的代码,包括所有特殊的行为。此 外,还检查了 Valgrind 是否有内存泄漏。为了保持高质量,该项目遵循核心基础设施倡议(CII) 的最佳实践

使用方法:

  1. #include "json.hpp"
  2. using json = nlohmann::json;

nlohman json

nlohman json为C++ JSON操作库,其主要设计目标如下:

已经有无数的JSON库,每个库都有其存在的理由。我们有这些设计目标:

  • 直观的语法。在像Python这样的语言中,JSON感觉就像是一级数据类型。我们使用现代C++的操作魔法来实现类似普通代码般的感觉。看看下面的例子,你就会明白我的意思。

  • 整合。我们的整个代码只有一个头文件json.hpp。没有库,没有子项目,没有依赖项,没有复杂的构建系统,仅仅使用标准C ++ 11编写。总而言之,不需要调整任何编译器标志或项目设置。

  • 压力测试。我们的类经过严格的单元测试,涵盖了100%的代码,包括所有特殊行为。此外,我们使用ValgrindClang Sanitizers做了检测,没有内存泄漏。还使用谷歌OSS-Fuzz对所有解析器进行24/7模糊测试,到目前为止有效地执行了数十亿次测试。为了保持高质量,该项目遵循核心基础设施倡议(CII)的最佳实践

对我们来说并不那么重要的其他方面:

  • 内存效率。每个JSON对象都有一个指针(联合的最大大小)和一个枚举元素(1个字节)的开销。默认泛化使用以下C ++数据类型:std::string用于字符串,int64_tuint64_tdouble用于数字,std::map用于对象,std::vector用于数组和bool用于布尔值。但是,您可以根据需要特化basic_json通用类。

  • 速度。肯定有更快的JSON库。但是,如果您的目标是通过添加单个头文件添加JSON支持来加速开发,那么这个库就是您的选择。如果您知道如何使用std::vectorstd::map,你就已经准备好了。

  • 整合
    json.hpp唯一所需的文件,在目录single_include/nlohmann这里。你需要添加:
  1. #include <nlohmann/json.hpp>
  2. // for convenience
  3. using json = nlohmann::json;

到您要处理JSON的文件时,设置必要的开关以启用C ++ 11(例如,-std=c++11对于GCC和Clang)。

您可以进一步使用文件include/nlohmann/json_fwd.hpp进行前向声明。安装json_fwd.hpp(作为cmake安装步骤的一部分),可以通过设置-DJSON_MultipleHeaders=ON来实现。


 

三、Json序列化实例

3.1 序列化实例1

  1. string func1(){
  2. json js;
  3. js["Msg_Type"]=2;
  4. js["From"]="Zhang San";
  5. js["To"]="Li Si";
  6. js["Message"]="Hey, BaBy!";
  7. string sendBuf = js.dump(); //转成字符串通过网络发送
  8. return sendBuf;
  9. }

输出结果:

3.2 序列化实例2

  1. string func2(){
  2. json js;
  3. //添加数组
  4. js["id"]={1,2,3,4,5};
  5. //添加键值对
  6. js["name"]="Zhang San";
  7. //添加对象
  8. js["msg"]={{"Zhang san","Hello World!"},{"Li Si","Hello China!"}};
  9. return js.dump();
  10. }

输出结果:

3.3 序列化实例3

  1. string func3(){
  2. json js;
  3. //序列化一个vector容器
  4. vector<int>v;
  5. v.push_back(1);
  6. v.push_back(2);
  7. v.push_back(5);
  8. js["list"]=v;
  9. //序列化一个map容器
  10. map<int,string>m;
  11. m.insert(make_pair(1,"黄山"));
  12. m.insert(make_pair(2,"华山"));
  13. m.insert(make_pair(3,"嵩山"));
  14. js["path"]=m;
  15. string SendBuf = js.dump(); //json数据对象->序列化 json字符串
  16. return SendBuf;
  17. }

输出结果:

四、Json反序列化实例

4.1 反序列化实例1

  1. string recBuf = func1();
  2. json jsbuf=json::parse(recBuf); //数据的反序列化 json字符串->json对象
  3. cout << jsbuf["Msg_Type"] << endl;
  4. cout << jsbuf["From"] << endl;
  5. cout << jsbuf["To"] << endl;
  6. cout << jsbuf["Message"] << endl;

4.2 反序列化实例2

  1. string recBuf = func2();
  2. json jsbuf=json::parse(recBuf); //数据的反序列化 json字符串->json对象
  3. auto arr=jsbuf["id"];
  4. cout << arr[2] << endl;
  5. auto jsmsg=jsbuf["msg"];
  6. cout << jsmsg["Zhang san"] << endl;
  7. cout << jsmsg["Li Si"] << endl;

4.3 反序列化实例3

  1. string recBuf = func3();
  2. json jsbuf=json::parse(recBuf); //数据的反序列化 json字符串->json对象
  3. vector<int> vec = jsbuf ["list"];
  4. for(int &v:vec){
  5. cout << v << " ";
  6. }
  7. cout << endl;
  8. map<int,string> mymap=jsbuf["path"];
  9. for(auto &p:mymap){
  10. cout << p.first << " " << p.second << endl;
  11. }

五、例子

除了下面的示例,您可能需要查看每个函数(包含单独代码示例)的文档(例如,查看emplace())。所有示例文件都可以自己编译和执行(例如,文件emplace.cpp)。

JSON作为一级的数据类型

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

假设您要创建如下的JSON对象:

  1. {
  2. "pi": 3.141,
  3. "happy": true,
  4. "name": "Niels",
  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. // create an empty structure (null)
  2. json j;
  3. // add a number that is stored as double (note the implicit conversion of j to an object)
  4. j["pi"] = 3.141;
  5. // add a Boolean that is stored as bool
  6. j["happy"] = true;
  7. // add a string that is stored as std::string
  8. j["name"] = "Niels";
  9. // add another null object by passing nullptr
  10. j["nothing"] = nullptr;
  11. // add an object inside the object
  12. j["answer"]["everything"] = 42;
  13. // add an array that is stored as std::vector (using an initializer list)
  14. j["list"] = { 1, 0, 2 };
  15. // add another object (using an initializer list of pairs)
  16. j["object"] = { {"currency", "USD"}, {"value", 42.99} };
  17. // instead, you could also write (which looks very similar to the JSON above)
  18. json j2 = {
  19. {"pi", 3.141},
  20. {"happy", true},
  21. {"name", "Niels"},
  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::arrayjson::object可满足您的需求:

  1. // a way to express the empty array []
  2. json empty_array_explicit = json::array();
  3. // ways to express the empty object {}
  4. json empty_object_implicit = json({});
  5. json empty_object_explicit = json::object();
  6. // a way to express an _array_ of key/value pairs [["currency", "USD"], ["value", 42.99]]
  7. json array_not_object = json::array({ {"currency", "USD"}, {"value", 42.99} });

序列化/反序列化

To/from strings

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

  1. // create object from string literal
  2. json j = "{ \"happy\": true, \"pi\": 3.141 }"_json;
  3. // or even nicer with a raw string literal
  4. auto j2 = R"(
  5. {
  6. "happy": true,
  7. "pi": 3.141
  8. }
  9. )"_json;

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

以上示例也可以使用json::parse()明确表达:

  1. // parse explicitly
  2. auto j3 = json::parse("{ \"happy\": true, \"pi\": 3.141 }");

您还可以获取JSON值的字符串表示形式(序列化):

  1. // explicit conversion to string
  2. std::string s = j.dump(); // {\"happy\":true,\"pi\":3.141}
  3. // serialization with pretty printing
  4. // pass in the amount of spaces to indent
  5. std::cout << j.dump(4) << std::endl;
  6. // {
  7. // "happy": true,
  8. // "pi": 3.141
  9. // }

注意序列化和赋值之间的区别:

  1. // store a string in a JSON value
  2. json j_string = "this is a string";
  3. // retrieve the string value (implicit JSON to std::string conversion)
  4. std::string cpp_string = j_string;
  5. // retrieve the string value (explicit JSON to std::string conversion)
  6. auto cpp_string2 = j_string.get<std::string>();
  7. // retrieve the serialized value (explicit JSON serialization)
  8. std::string serialized_string = j_string.dump();
  9. // output of original string
  10. std::cout << cpp_string << " == " << cpp_string2 << " == " << j_string.get<std::string>() << '\n';
  11. // output of serialized value
  12. std::cout << j_string << " == " << serialized_string << std::endl;

.dump()始终返回序列化值,而.get<std::string>()返回最初存储的字符串值。

请注意,该库仅支持UTF-8。当您在库中存储具有不同编码的字符串时,调用dump()可能会抛出异常。

To/from streams (e.g. files, string streams)

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

  1. // deserialize from standard input
  2. json j;
  3. std::cin >> j;
  4. // serialize to standard output
  5. std::cout << j;
  6. // the setw manipulator was overloaded to set the indentation for pretty printing
  7. std::cout << std::setw(4) << j << std::endl;

这些运算符适用于std::istreamstd::ostream的任何子类。这是文件的类似示例:

  1. // read a JSON file
  2. std::ifstream i("file.json");
  3. json j;
  4. i >> j;
  5. // write prettified JSON to another file
  6. std::ofstream o("pretty.json");
  7. o << std::setw(4) << j << std::endl;

请注意,为failbit设置异常位不适用此用例。由于使用了noexcept说明符,它将导致程序终止。

从迭代器范围读取

您还可以从迭代器范围解析JSON; 也就是说,其存储内容为连续的字节序列,可从迭代器访问的任何容器,例如std::vector<std::uint8_t>:

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

您也可以去掉范围[begin, end)操作符:

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

STL-like access

我们定义的JSON类的行为与STL容器一样。事实上,它遵循ReversibleContainer规范。

  1. // create an array using push_back
  2. json j;
  3. j.push_back("foo");
  4. j.push_back(1);
  5. j.push_back(true);
  6. // also use emplace_back
  7. j.emplace_back(1.78);
  8. // iterate the array
  9. for (json::iterator it = j.begin(); it != j.end(); ++it) {
  10. std::cout << *it << '\n';
  11. }
  12. // range-based for
  13. for (auto& element : j) {
  14. std::cout << element << '\n';
  15. }
  16. // getter/setter
  17. const std::string tmp = j[0];
  18. j[1] = 42;
  19. bool foo = j.at(2);
  20. // comparison
  21. j == "[\"foo\", 1, true]"_json; // true
  22. // other stuff
  23. j.size(); // 3 entries
  24. j.empty(); // false
  25. j.type(); // json::value_t::array
  26. j.clear(); // the array is empty again
  27. // convenience type checkers
  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. // create an object
  35. json o;
  36. o["foo"] = 23;
  37. o["bar"] = false;
  38. o["baz"] = 3.141;
  39. // also use emplace
  40. o.emplace("weather", "sunny");
  41. // special iterator member functions for objects
  42. for (json::iterator it = o.begin(); it != o.end(); ++it) {
  43. std::cout << it.key() << " : " << it.value() << "\n";
  44. }
  45. // find an entry
  46. if (o.find("foo") != o.end()) {
  47. // there is an entry with key "foo"
  48. }
  49. // or simpler using count()
  50. int foo_present = o.count("foo"); // 1
  51. int fob_present = o.count("fob"); // 0
  52. // delete an entry
  53. o.erase("foo");

从STL容器转换

任何序列容器(std::array,std::vector,std::deque,std::forward_list,std::list),其值可以被用于构建JSON值(例如,整数,浮点数,布尔值,字符串类型,或者在本节中描述的STL容器)都可被用于创建JSON数组。这同样适用于类似的关联容器(std::set,std::multiset,std::unordered_set,std::unordered_multiset),但是在这些情况下,数组中的元素的顺序取决于元素是如何在各个STL容器排序。

  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::map,std::multimap,std::unordered_map,std::unordered_multimap),其键可以构造一个std::string,并且其值可以被用于构建JSON值(见上文示例)可用于创建一个JSON对象。请注意,在多映射的情况下,JSON对象中只使用一个键,值取决于STL容器的内部顺序。

  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}

任意类型转换

任何类型都可以用JSON序列化,而不仅仅是STL容器和标量类型。通常,您将遵循这些准绳来做事:

  1. namespace ns {
  2. // a simple struct to model a person
  3. struct person {
  4. std::string name;
  5. std::string address;
  6. int age;
  7. };
  8. }
  9. ns::person p = {"Ned Flanders", "744 Evergreen Terrace", 60};
  10. // convert to JSON: copy each value into the JSON object
  11. json j;
  12. j["name"] = p.name;
  13. j["address"] = p.address;
  14. j["age"] = p.age;
  15. // ...
  16. // convert from JSON: copy each value from the JSON object
  17. ns::person p {
  18. j["name"].get<std::string>(),
  19. j["address"].get<std::string>(),
  20. j["age"].get<int>()
  21. };

这个代码没有问题,但是有点啰嗦,我们有一个更好的办法:

  1. // create a person
  2. ns::person p {"Ned Flanders", "744 Evergreen Terrace", 60};
  3. // conversion: person -> json
  4. json j = p;
  5. std::cout << j << std::endl;
  6. // {"address":"744 Evergreen Terrace","age":60,"name":"Ned Flanders"}
  7. // conversion: json -> person
  8. ns::person p2 = j;
  9. // that's it
  10. assert(p == p2);

基本用法

要使其适用于您的某种类型,您只需提供两个功能:

  1. using nlohmann::json;
  2. namespace ns {
  3. void to_json(json& j, const person& p) {
  4. j = json{{"name", p.name}, {"address", p.address}, {"age", p.age}};
  5. }
  6. void from_json(const json& j, person& p) {
  7. p.name = j.at("name").get<std::string>();
  8. p.address = j.at("address").get<std::string>();
  9. p.age = j.at("age").get<int>();
  10. }
  11. } // namespace ns

就这么简单!json使用您的类型调用构造函数时,您的自定义方法to_json将被自动调用。同样,在调用get<your_type>()时,from_json将被自动调用。
一些重要的点:

  • 那些方法必须在你的类型的命名空间(可以是全局命名空间)中,否则库将无法找到它们(在这个例子中,person在命名空间中ns中定义)。
  • 在您使用隐式转换的地方,那些方法必须是有效的(例如:正确的头文件必须被包含)查看问题1108,了解可能发生的错误。
  • 使用get<your_type>()时,your_type 必须DefaultConstructible。(后面会描述一种可以绕过这个要求的方法。)
  • 在函数from_json中,使用函数at()来访问对象值而不是operator[]。如果某个键不存在,则at抛出一个可以处理的异常,然而operator[]显示未定义的行为。
  • 如果您的类型包含多个operator=定义,则代码your_variable = your_json; 可能无法编译。您需要改写成your_variable = your_json.get<decltype your_variable>();
  • 您不需要为STL类型添加序列化或反序列化程序,例如std::vector:库已经实现了这些。
  • 注意函数from_json/ to_json的定义顺序:如果一个类型B有类型的成员A,你必须在定义to_json(B)之前,定义to_json(A)。请查看问题561以获取更多详细信息。


 

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/煮酒与君饮/article/detail/962710
推荐阅读
相关标签
  

闽ICP备14008679号