使用CJSON/Nlohmann:快速简便地在C/C++中处理JSON数据


概述

纯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对象结构。

解析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数据的验证和测试。

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

泡沫o0

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值