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

简介: 使用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对象结构。
• 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数据的验证和测试。

目录
相关文章
|
1月前
|
JSON 程序员 数据格式
深入探索 “JSON for Modern C++“:安装、构建与应用
深入探索 “JSON for Modern C++“:安装、构建与应用
40 0
|
1月前
|
存储 前端开发 Java
【C++ 多线程 】C++并发编程:精细控制数据打印顺序的策略
【C++ 多线程 】C++并发编程:精细控制数据打印顺序的策略
45 1
|
1月前
|
JSON JavaScript 前端开发
C++ 智能指针与 JSON 处理:高级编程技巧与常见问题解析
C++ 智能指针与 JSON 处理:高级编程技巧与常见问题解析
269 0
|
1月前
|
存储 JSON 算法
C++ JSON库 nlohmann::basic_json::boolean_t 的用法
C++ JSON库 nlohmann::basic_json::boolean_t 的用法
35 0
|
2天前
|
JSON 数据可视化 定位技术
python_将包含汉字的字典数据写入json(将datav的全省数据中的贵州区域数据取出来)
python_将包含汉字的字典数据写入json(将datav的全省数据中的贵州区域数据取出来)
7 0
|
14天前
|
存储 JSON JavaScript
「Python系列」Python JSON数据解析
在Python中解析JSON数据通常使用`json`模块。`json`模块提供了将JSON格式的数据转换为Python对象(如列表、字典等)以及将Python对象转换为JSON格式的数据的方法。
31 0
|
18天前
|
存储 JSON 数据挖掘
python逐行读取txt文本中的json数据,并进行处理
Python代码示例演示了如何读取txt文件中的JSON数据并处理。首先,逐行打开文件,然后使用`json.loads()`解析每一行。接着,处理JSON数据,如打印特定字段`name`。异常处理包括捕获`JSONDecodeError`和`KeyError`,确保数据有效性和字段完整性。将`data.txt`替换为实际文件路径运行示例。
14 2
|
1月前
|
JSON JavaScript 数据格式
【深入探究C++ JSON库】解析JSON元素的层级管理与遍历手段
【深入探究C++ JSON库】解析JSON元素的层级管理与遍历手段
95 2
|
1月前
|
XML JSON API
深入解析C++ JSON库:nlohmann::json:: parse的内部机制与应用
深入解析C++ JSON库:nlohmann::json:: parse的内部机制与应用
52 0
|
1月前
|
存储 缓存 安全
【C/C++ 关键字 存储类说明符】C/C++ 的mutable 关键字 忽略对该数据成员的常量性检查在const函数中修改变量值
【C/C++ 关键字 存储类说明符】C/C++ 的mutable 关键字 忽略对该数据成员的常量性检查在const函数中修改变量值
29 0