概述
纯C环境中使用
cjson
库,C++环境中也可以使用nlohmann
库,本文介绍基本的使用场景,如需更详细的介绍可以查看库官方文档。
nlohmann:
nlohmann库(https://github.com/nlohmann/json)提供了丰富而且符合直觉的接口(https://json.nlohmann.me/api/basic_json/),只需导入头文件即可使用,方便整合到项目中。
CJSON:
JSON: JavaScript Object Notation(JavaScript 对象表示法),是轻量级的存储和交换文本信息的语法,类似 XML . 特点是纯文本(纯字符串)、层级结构、使用数组。
cJson:一个基于 C 语言的 Json 库,它是一个开源项目,github 下载地址:https://github.com/DaveGamble/cJSON
cJson库组成:主要的文件有两个,一个 cJSON.c 一个 cJSON.h。使用时,将头文件 include 进去即可CJSON和nlohmann都是C/C++中常用的JSON库,它们各有优缺点,下面是一些比较:
- 功能特性:nlohmann库提供的功能更加丰富,包括JSON的解析、序列化、查询、遍历等功能,而CJSON库则相对简单,仅提供基本的JSON解析和生成功能。
- 性能:CJSON库是一个轻量级库,相对来说性能更好,而nlohmann库则提供了更多的功能,相对来说会有一些性能损失。
- 依赖性:CJSON库不依赖任何其他库,可以直接使用,而nlohmann库依赖于C++11标准库,需要在编译时进行指定。
- 可移植性:CJSON库不依赖任何其他库,可以在任何平台上使用,而nlohmann库则需要C++11支持,不支持C++11的平台可能会出现问题。
综上所述,CJSON和nlohmann库各有优缺点,选择哪个库取决于具体应用场景和需求。如果需要使用更多的功能,可以选择nlohmann库,如果需要更好的性能和可移植性,可以选择CJSON库。
cjson
typedef struct cJSON { //cJSON结构体 struct cJSON*next,*prev; /* 遍历数组或对象链的前向或后向链表指针*/ struct cJSON *child; /*数组或对象的孩子节点*/ int type; /* key的类型*/ char *valuestring; /*字符串值*/ int valueint; /* 整数值*/ double valuedouble; /* 浮点数值*/ char *string; /* key的名字*/ } cJSON_st;
- 基本操作
- 从(字符指针)缓冲区中解析出JSON结构
extern cJSON *cJSON_Parse(const char *value); //解析一块JSON数据返回cJSON结构, 在使用完之后调用cJSON_Delete函数释放json对象结构。 • 1 • 2
解析JSON数据包,并按照cJSON结构体的结构序列化整个数据包。
使用该函数会通过malloc函数在内存中开辟一个空间,使用完成需要手动释放。
- 转成成JS字符串(将传入的JSON结构转化为字符串)
extern char *cJSON_Print(cJSON *item); //可用于输出到输出设备, 使用完之后free(char *) cJSON_PrintUnformatted(cJSON *item); //类似,没有格式,即转换出的字符串中间不会有"\n" "\t"之类的东西存在.
- 将JSON结构所占用的数据空间释放
void cJSON_Delete(cJSON *c) //会将其下的所有节点的资源一并释放掉!!
- JSON 值的创建
- 创建一个值类型的数据
extern cJSON *cJSON_CreateNumber(double num); //创建 extern cJSON *cJSON_CreateString(const char *string); //创建 extern cJSON *cJSON_CreateArray(void); //创建json数组
- 创建一个对象(文档)
extern cJSON *cJSON_CreateObject(void); //创建一个根数据项,之后便可向该根数据项中添加string或int等内容
- 数组创建以及添加
cJSON *cJSON_CreateIntArray(const int *numbers,int count); void cJSON_AddItemToArray(cJSON *array, cJSON *item);
- JSON嵌套
- 向对象中增加键值对
cJSON_AddItemToObject(root, "rows", 值类型数据相关函数());
- 向对象中增加数组
cJSON_AddItemToObject(root, "rows", cJSON_CreateArray()); //创建一个根数据项,之后便可向该根数据项中添加string或int等内容
- 向数组中增加对象
cJSON_AddItemToArray(rows, cJSON_CreateObject()); //向json数组中增加元素/对象
- 几个能提高操作效率的宏函数
#define cJSON_AddNumberToObject(object,name,n) cJSON_AddItemToObject(object, name,cJSON_CreateNumber(n)) //向json对象中添加数字,节点名and节点值 #define cJSON_AddStringToObject(object,name,s) cJSON_AddItemToObject(object, name, cJSON_CreateString(s)) // 向json对象中添加字符串
- 查找JSON值
- 根据键找json结点
extern cJSON *cJSON_GetObjectItem(cJSON *object,const char*string) //从cJSON结构体中查找某个子节点名称(键名称),如果查找成功可把该子节点序列化到cJSON结构体中。
- 判断是否有key是string的项
extern int cJSON_HasObjectItem(cJSON *object,const char *string){ return cJSON_GetObjectItem(object,string)?1:0; } //如果有返回1 否则返回0
- 返回数组结点array中成员的个数
extern int cJSON_GetArraySize(cJSON *array);
- 根据数组下标index取array数组结点的第index个成员
extern cJSON *cJSON_GetArrayItem(cJSON *array,int index); //返回该成员节点
- 遍历数组
#define cJSON_ArrayForEach(pos, head) for(pos = (head)->child; pos != NULL; pos = pos->next) cJSON_ReplaceItemInObject(json,"data",cJSON_CreateString("hello")) //用于代替json对象中data元组的值
nlohmann
nlohmann库是一个C++ JSON库,用于处理JSON数据的解析、序列化、查询、遍历等操作。它是一个轻量级的库,易于使用和集成到C++项目中。
nlohmann库的作用是为C++开发人员提供一种方便、快捷、安全的方式来处理JSON数据。它的API简单易用,可以帮助开发人员快速开发出高效、可靠的JSON数据处理程序,减少开发时间和开发成本。
nlohmann库在C++中处理JSON数据的优势主要有以下几点:
简单易用:nlohmann库的API简单直观,易于学习和使用。可以帮助开发人员快速开发出高效、可靠的JSON数据处理程序。
高效性能:nlohmann库是一个轻量级库,可以提供高效的JSON数据解析和序列化功能。它使用了现代C++的一些特性来提高性能,如移动语义、模板编程等。
标准兼容性:nlohmann库完全符合C++11标准,支持所有标准C++11容器,并且可以与现有的C++代码无缝集成。
跨平台性:nlohmann库可以在多种平台上使用,包括Windows、Linux、macOS等。
可扩展性:nlohmann库可以轻松地扩展到其他JSON数据处理库中,例如使用第三方序列化器和反序列化器等。
总之,nlohmann库是一个非常有用的C++ JSON库,它可以帮助开发人员快速开发出高效、可靠的JSON数据处理程序,减少开发时间和开发成本。
nlohmann库的基本功能
字符串解析
#include <iostream> #include <nlohmann/json.hpp> using json = nlohmann::json; int main() { std::string jsonString = R"({"name":"John","age":30,"city":"New York"})"; // 解析JSON数据 json data = json::parse(jsonString); // 输出JSON数据 std::cout << "name: " << data["name"] << std::endl; std::cout << "age: " << data["age"] << std::endl; std::cout << "city: " << data["city"] << std::endl; return 0; }
序列化为字符串
#include <iostream> #include <nlohmann/json.hpp> using json = nlohmann::json; int main() { std::string jsonString = R"({"name":"John","age":30,"city":"New York"})"; // 解析JSON数据 json data = json::parse(jsonString); // 查询是否存在某个属性 if (data.find("name") != data.end()) { std::cout << "name exists" << std::endl; } else { std::cout << "name does not exist" << std::endl; } return 0; }
查询一个JSON对象中是否存在某个属性
#include <iostream> #include <nlohmann/json.hpp> using json = nlohmann::json; int main() { // 创建JSON数据对象 json data; data["name"] = "John"; data["age"] = 30; data["city"] = "New York"; // 序列化JSON数据 std::string jsonString = data.dump(); // 输出序列化后的JSON数据 std::cout << "JSON data: " << jsonString << std::endl; return 0; }
遍历一个JSON数组
#include <iostream> #include <nlohmann/json.hpp> using json = nlohmann::json; int main() { std::string jsonString = R"([{"name":"John","age":30},{"name":"Mary","age":25},{"name":"Peter","age":40}])"; // 解析JSON数据 json data = json::parse(jsonString); // 遍历JSON数组 for (const auto& item : data) { std::cout << "name: " << item["name"] << ", age: " << item["age"] << std::endl; } return 0; }
nlohmann库的高级功能
将JSON数据压缩为Gzip格式
#include <iostream> #include <nlohmann/json.hpp> #include <fstream> #include <sstream> #include <zlib.h> using json = nlohmann::json; int main() { // 创建JSON数据对象 json data; data["name"] = "John"; data["age"] = 30; data["city"] = "New York"; // 将JSON数据压缩为Gzip格式 std::stringstream compressedData; gzFile file = gzopen("data.gz", "wb"); std::string jsonString = data.dump(); gzwrite(file, jsonString.c_str(), jsonString.size()); gzclose(file); // 输出压缩后的数据 std::ifstream compressedFile("data.gz"); std::stringstream compressedBuffer; compressedBuffer << compressedFile.rdbuf(); std::cout << "Compressed data: " << compressedBuffer.str() << std::endl; return 0; }
将JSON数据加密为Base64格式
#include <iostream> #include <nlohmann/json.hpp> #include <openssl/bio.h> #include <openssl/evp.h> using json = nlohmann::json; int main() { // 创建JSON数据对象 json data; data["name"] = "John"; data["age"] = 30; data["city"] = "New York"; // 将JSON数据加密为Base64格式 std::string jsonString = data.dump(); std::string base64String; BIO *bio, *b64; BUF_MEM *bufferPtr; b64 = BIO_new(BIO_f_base64()); BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL); bio = BIO_new(BIO_s_mem()); BIO_push(b64, bio); BIO_write(b64, jsonString.c_str(), jsonString.size()); BIO_flush(b64); BIO_get_mem_ptr(b64, &bufferPtr); base64String = std::string(bufferPtr->data, bufferPtr->length); BIO_free_all(b64); // 输出加密后的数据 std::cout << "Base64 data: " << base64String << std::endl; return 0; }
将JSON数据缓存到内存
#include <iostream> #include <nlohmann/json.hpp> using json = nlohmann::json; int main() { // 创建JSON数据对象 json data; data["name"] = "John"; data["age"] = 30; data["city"] = "New York"; // 将JSON数据缓存到内存 std::vector<uint8_t> buffer; nlohmann::json::to_cbor(data, buffer); // 输出缓存后的数据 for (const auto& item : buffer) { std::cout << std::hex << item << " "; } std::cout << std::endl; return 0; }
从Base64格式的数据反序列化
#include <iostream> #include <nlohmann/json.hpp> #include <openssl/bio.h> #include <openssl/evp.h> using json = nlohmann::json; int main() { // Base64格式的数据 std::string base64String = "eyJhZ2UiOjMwLCJuYW1lIjoiSm9obiIsImNpdHkiOiJOZXcgWW9yayJ9"; // 将Base64格式的数据解密为JSON数据 std::string jsonString; BIO *bio, *b64; b64 = BIO_new(BIO_f_base64()); BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL); bio = BIO_new_mem_buf(base64String.c_str(), base64String.size()); bio = BIO_push(b64, bio); char buffer[1024]; int length = 0; while ((length = BIO_read(bio, buffer, sizeof(buffer))) > 0) { jsonString.append(buffer, length); } BIO_free_all(bio); json data = json::parse(jsonString); // 输出反序列化后的JSON数据 std::cout << "name: " << data["name"] << ", age: " << data["age"] << ", city: " << data["city"] << std::endl; return 0; }
nlohmann库的性能优化
使用迭代器
使用迭代器 在遍历JSON数据时,使用迭代器可以提高性能,因为迭代器不需要创建临时对象,可以直接访问JSON数据的元素。例如:
#include <iostream> #include <nlohmann/json.hpp> using json = nlohmann::json; int main() { std::string jsonString = R"([{"name":"John","age":30},{"name":"Mary","age":25},{"name":"Peter","age":40}])"; // 解析JSON数据 json data = json::parse(jsonString); // 使用迭代器遍历JSON数组 for (json::iterator it = data.begin(); it != data.end(); ++it) { std::cout << "name: " << (*it)["name"] << ", age: " << (*it)["age"] << std::endl; } return 0; }
预分配空间
使用预分配内存 在创建JSON数据时,可以预先分配足够的内存空间,以避免频繁的内存分配和释放,从而提高性能。
#include <iostream> #include <nlohmann/json.hpp> using json = nlohmann::json; int main() { // 预分配1000个对象的内存空间 json data = json::array(); data.reserve(1000); // 添加1000个对象 for (int i = 0; i < 1000; ++i) { json item; item["name"] = "John"; item["age"] = 30; item["city"] = "New York"; data.push_back(item); } return 0; }
并行计算
使用并行计算 在处理大型JSON数据时,可以使用并行计算来提高处理效率。例如可以使用C++11提供的线程库或者OpenMP库来并行处理JSON数据。以下是一个使用OpenMP库来并行遍历JSON数组的示例代码:
#include <iostream> #include <nlohmann/json.hpp> #include <omp.h> using json = nlohmann::json; int main() { std::string jsonString = R"([{"name":"John","age":30},{"name":"Mary","age":25},{"name":"Peter","age":40}])"; // 解析JSON数据 json data = json::parse(jsonString); // 并行遍历JSON数组 #pragma omp parallel for for (int i = 0; i < data.size(); ++i) { std::cout << "name: " << data[i]["name"] << ", age: " << data[i]["age"] << std::endl; } return 0; }
nlohmann库最佳实践
在实际项目中使用nlohmann库,需要注意以下几点最佳实践:
- 规划JSON数据结构
在使用nlohmann库处理JSON数据时,首先需要规划好JSON数据的结构。可以通过对业务需求的分析,确定JSON数据的键名和键值类型,并设计出符合需求的JSON数据结构。此外,还可以使用JSON Schema等工具来定义JSON数据的结构,以便于后续的验证和测试。
- 处理JSON数据的异常情况
在处理JSON数据时,可能会出现异常情况,例如JSON数据格式错误、键名或键值类型不匹配等。为了避免这些异常情况对程序的影响,需要在代码中进行相应的异常处理。nlohmann库提供了多种异常处理方式,包括抛出异常、返回错误码等。
- 进行JSON数据的验证和测试
为了保证JSON数据的正确性和稳定性,需要进行相应的验证和测试。可以使用JSON Schema等工具来验证JSON数据的结构和内容是否符合规范。同时,还可以编写单元测试和集成测试来验证JSON数据的处理逻辑和异常处理是否正确。nlohmann库提供了多种测试工具和框架,例如Google Test、Catch2等。
综上所述,规划JSON数据结构、处理JSON数据的异常情况和进行JSON数据的验证和测试是使用nlohmann库的最佳实践。这些实践可以有效提高代码的稳定性和可维护性,保证JSON数据的正确性和一致性。
nlohmann库与其他库的比较
nlohmann库是C++中比较流行的JSON库之一,与其他常用的JSON库如CJSON、RapidJSON、Boost.JSON等相比,有以下几点优缺点:
- CJSON
CJSON是一个轻量级的C语言JSON库,只有一个头文件和源文件,易于集成到项目中。相比nlohmann库,CJSON的功能较为简单,只支持JSON的基本操作,例如解析、生成、查询等。因此,对于简单的JSON操作,可以选择使用CJSON来实现。
- RapidJSON
RapidJSON是一个高性能的C++ JSON库,具有快速解析和生成JSON数据的能力。相比nlohmann库,RapidJSON的解析速度更快,但是在内存使用和代码复杂度方面略逊一筹。因此,对于需要高性能的JSON操作,可以选择使用RapidJSON来实现。
- Boost.JSON
Boost.JSON是一个基于Boost库的JSON库,具有良好的跨平台性和可移植性。相比nlohmann库,Boost.JSON的API较为复杂,需要一定的学习成本,并且不支持C++11之前的版本。因此,对于需要跨平台和可移植性的JSON操作,可以选择使用Boost.JSON来实现。
综上所述,nlohmann库与其他常用的JSON库相比,具有易用性、可读性和可维护性强的特点,同时也具有较好的性能表现。因此,对于大多数的JSON操作场景,建议选择使用nlohmann库来实现。当需要更高的性能时,可以考虑使用RapidJSON;当需要跨平台和可移植性时,可以考虑使用Boost.JSON;当需要简单的JSON操作时,可以考虑使用CJSON。
nlohmann库使用代码综合示例
#pragma once #include <iostream> #include <fstream> #include <string> #include <nlohmann/json.hpp> static bool JsonConfigInit(const std::string& config_file) bool ret = false; std::ifstream cfg_file(config_file); if (!cfg_file.is_open()) { nlohmann::json configjson; //创建一个空结构 configjson["config_settings"] = { {"bool_value", true},{"file_size", 15},{"info_file", "./tmp.ini"}};//对对象进行初始化 std::ofstream(config_file.c_str()) << configjson; std::cout << "create config json file"<< std::endl; return false; } try{ std::cout << "JsonConfigInit from \" " << config_file<< "\" "<< std::endl; const nlohmann::json& file_json = nlohmann::json::parse(cfg_file); //判断首节点是否存在 if(file_json.is_null() || (file_json.contains("config_settings") == false ) || file_json.at("config_settings").size() == 0){ ret = false; goto json_end; } nlohmann::json set_json = file_json.at("config_settings"); // 获取相应的键值对,get_to必须保证双方类型一致,否则极易出现段错误 bool m_json_bool; int m_json_number; std::string m_json_string ; if(set_json.at("bool_value").is_boolean()){ set_json.at("bool_value").get_to(m_json_bool); } if(set_json.at("file_size").is_number()){ set_json.at("file_size").get_to(m_json_number); } if(set_json.at("info_file").is_string()){ set_json.at("info_file").get_to(m_json_string); } ret = true; } } catch (nlohmann::json::parse_error& ex){ std::cerr << "JsonConfigInit failed:parse error ! ! reason: [" << ex.what() << "]"<< std::endl; } catch (nlohmann::json::exception& ex){ std::cerr << "JsonConfigInit parse_json parse fail! reason: [" << ex.what() << "]"<< std::endl; } json_end: cfg_file.close(); return ret; }
总结
在C/C++中处理JSON数据是一个常见的需求,而CJSON和nlohmann库都是比较流行的JSON库,可以快速简便地实现JSON数据的解析、生成和操作。
CJSON是一个轻量级的C语言JSON库,只有一个头文件和源文件,易于集成到项目中。可以通过CJSON库提供的API来实现JSON数据的解析、生成和操作。CJSON库的API比较简单,只支持JSON的基本操作,例如解析、生成、查询等。对于简单的JSON操作,可以选择使用CJSON来实现。
nlohmann库是C++中比较流行的JSON库之一,具有易用性、可读性和可维护性强的特点,同时也具有较好的性能表现。可以通过nlohmann库提供的API来实现JSON数据的解析、生成和操作。nlohmann库的API使用起来比较简单,支持STL风格的语法,可以方便地进行JSON数据的操作。
在使用CJSON和nlohmann库处理JSON数据时,需要注意以下几点:
规划JSON数据结构,设计好JSON数据的键名和键值类型,并确定JSON数据的结构。
处理JSON数据的异常情况,例如JSON数据格式错误、键名或键值类型不匹配等,需要进行相应的异常处理。
进行JSON数据的验证和测试,可以使用JSON Schema等工具来验证JSON数据的结构和内容是否符合规范。同时,还可以编写单元测试和集成测试来验证JSON数据的处理逻辑和异常处理是否正确。
综上所述,使用CJSON和nlohmann库可以快速简便地在C/C++中处理JSON数据。对于简单的JSON操作,可以选择使用CJSON来实现;对于需要易用性、可读性和可维护性强的JSON操作,可以选择使用nlohmann库来实现。无论选择哪种库,都需要注意规划JSON数据结构、处理JSON数据的异常情况和进行JSON数据的验证和测试。