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

目录
相关文章
|
3天前
|
JSON NoSQL MongoDB
实时计算 Flink版产品使用合集之要将收集到的 MongoDB 数据映射成 JSON 对象而非按字段分割,该怎么操作
实时计算Flink版作为一种强大的流处理和批处理统一的计算框架,广泛应用于各种需要实时数据处理和分析的场景。实时计算Flink版通常结合SQL接口、DataStream API、以及与上下游数据源和存储系统的丰富连接器,提供了一套全面的解决方案,以应对各种实时计算需求。其低延迟、高吞吐、容错性强的特点,使其成为众多企业和组织实时数据处理首选的技术平台。以下是实时计算Flink版的一些典型使用合集。
34 1
|
5天前
|
存储 JSON 数据处理
从JSON数据到Pandas DataFrame:如何解析出所需字段
从JSON数据到Pandas DataFrame:如何解析出所需字段
19 1
|
5天前
|
XML JSON API
转Android上基于JSON的数据交互应用
转Android上基于JSON的数据交互应用
11 1
|
5天前
|
JSON Java Linux
【探索Linux】P.30(序列化和反序列化 | JSON序列化库 [ C++ ] )
【探索Linux】P.30(序列化和反序列化 | JSON序列化库 [ C++ ] )
23 2
|
5天前
|
JSON JavaScript Java
从前端Vue到后端Spring Boot:接收JSON数据的正确姿势
从前端Vue到后端Spring Boot:接收JSON数据的正确姿势
26 0
|
5天前
|
JSON 数据格式 Python
Python标准库中包含了json模块,可以帮助你轻松处理JSON数据
【4月更文挑战第30天】Python的json模块简化了JSON数据与Python对象之间的转换。使用`json.dumps()`可将字典转为JSON字符串,如`{&quot;name&quot;: &quot;John&quot;, &quot;age&quot;: 30, &quot;city&quot;: &quot;New York&quot;}`,而`json.loads()`则能将JSON字符串转回字典。通过`json.load()`从文件读取JSON数据,`json.dump()`则用于将数据写入文件。
18 1
|
5天前
|
JSON 数据格式 Python
Python处理JSON数据
【4月更文挑战第30天】该内容介绍了Python处理JSON数据的三个方法:1)使用`json.loads()`尝试解析字符串以验证其是否为有效JSON,通过捕获`JSONDecodeError`异常判断有效性;2)通过`json.dumps()`的`indent`参数格式化输出JSON数据,使其更易读;3)处理JSON中的日期,利用`dateutil`库将日期转换为字符串进行序列化和反序列化。
23 4
|
5天前
|
存储 JSON 数据处理
|
5天前
|
JSON 数据可视化 定位技术
python_将包含汉字的字典数据写入json(将datav的全省数据中的贵州区域数据取出来)
python_将包含汉字的字典数据写入json(将datav的全省数据中的贵州区域数据取出来)
19 0
|
5天前
|
C++
C++ 访问说明符详解:封装数据,控制访问,提升安全性
C++ 中的访问说明符(public, private, protected)用于控制类成员的可访问性,实现封装,增强数据安全性。public 成员在任何地方都可访问,private 只能在类内部访问,protected 则允许在类及其派生类中访问。封装提供数据安全性、代码维护性和可重用性,通过 setter/getter 方法控制对私有数据的访问。关注公众号 `Let us Coding` 获取更多内容。
27 1