跳至正文
老丹的足迹 —— 代码写给机器,游记写给自己,感悟写给时间
老丹的足迹 老丹的足迹
老丹的足迹 老丹的足迹
  • 首页
  • 示例页面
  • 首页
  • 示例页面
老丹的足迹 老丹的足迹
老丹的足迹 老丹的足迹
  • 首页
  • 示例页面
  • 首页
  • 示例页面

C/C++ 开源JSON库详解:cJSON、json-c、jsoncpp

一、概述

JSON(JavaScript Object Notation)是现代软件开发中最通用的数据交换格式之一。在C/C++生态中,有三个非常成熟且广泛使用的开源JSON库:cJSON、json-c 和 jsoncpp。它们分别面向不同的应用场景,各有特色。

库名开发语言核心定位典型应用场景
cJSONC (ANSI C)极轻量、单文件集成嵌入式系统、物联网设备、RTOS
json-cC引用计数、稳定可靠Linux系统组件、长期维护的C项目
jsoncppC++现代C++风格、开发友好C++应用程序、后台服务、客户端软件

二、各库详细介绍

1. cJSON —— 嵌入式开发的黄金标准

背景与定位

cJSON由Dave Gamble开发,是目前GitHub上star数最多的C语言JSON库之一。它被广泛认为是嵌入式/物联网领域的事实标准,据称超过90%的物联网设备相关项目都在使用它。

核心优势

① 极致的轻量性

  • 整个库仅由两个文件组成:cJSON.c 和 cJSON.h
  • 编译后二进制体积极小(通常几十KB)
  • 可以轻松地直接拖入任何C/C++项目,无需复杂的构建配置

② 极佳的移植性

  • 完全遵循ANSI C (C89)标准
  • 不依赖任何第三方库
  • 可以在MCU、RTOS、甚至内核态等极度受限的环境中运行

③ 简单直观的API设计

  • API函数数量少,学习成本低
  • 核心操作只有:Create、Add、Print、Parse、Delete
数据结构原理

cJSON使用一个巧妙的结构体来同时表示JSON节点的类型、值和层级关系:

typedef struct cJSON {
    struct cJSON *next, *prev;  // 同级节点用双向链表连接
    struct cJSON *child;        // 子节点指针(构成树状结构)
    int type;                   // 节点类型标识
    char *valuestring;          // 字符串类型的值
    double valuedouble;         // 数值类型的值
    char *string;               // 当前节点的键名
} cJSON;

理解这个结构是掌握cJSON的关键:它本质上是用链表+树的方式在内存中组织JSON数据。

使用示例
#include "cJSON.h"

// 创建对象:{"name":"张三","age":25}
cJSON *root = cJSON_CreateObject();
cJSON_AddStringToObject(root, "name", "张三");
cJSON_AddNumberToObject(root, "age", 25);

// 序列化为字符串
char *json_str = cJSON_Print(root);
printf("%s\n", json_str);

// 解析JSON
cJSON *parsed = cJSON_Parse(json_str);
cJSON *name = cJSON_GetObjectItem(parsed, "name");
printf("name: %s\n", name->valuestring);

// 释放内存(非常重要!)
cJSON_Delete(root);
cJSON_Delete(parsed);
free(json_str);
局限性
  • 手动内存管理:必须记得调用cJSON_Delete()和free(),否则会内存泄漏
  • 非线程安全:多线程环境下需要自行加锁
  • 性能中等:不是最快的库,但足以满足绝大多数嵌入式场景
适用场景总结
✅ 适合❌ 不适合
单片机/嵌入式系统追求极致性能的高并发服务
物联网设备大型C++项目(推荐用jsoncpp)
资源受限环境需要线程安全的多线程程序
需要极简集成的项目

2. json-c —— C语言世界的稳定老兵

背景与定位

json-c是一个历史悠久的C语言JSON库,被广泛集成在各大Linux发行版的官方软件源中(可通过apt-get install libjson-c-dev直接安装)。它的设计目标是成为Linux/Unix系统级应用中稳定、可靠的JSON处理方案。

核心优势

① 引用计数内存管理
这是json-c最核心、最独特的设计。所有JSON对象都被封装在json_object结构体中,通过引用计数来自动管理生命周期:

  • json_object_get(obj) → 引用计数+1
  • json_object_put(obj) → 引用计数-1,减到0时自动释放

这套机制在复杂的C项目中能有效减少内存泄漏的风险。

② 极高的稳定性

  • API经过了十几年的考验,变化极小
  • 如果你的项目需要维护5年、10年甚至更久,json-c是非常可靠的选择
  • 被大量Linux系统组件(如网络管理器、系统服务等)所采用

③ 功能完整

  • 支持JSON的完整数据类型
  • 提供格式化输出(带缩进)和紧凑输出
  • 支持从文件直接解析
使用示例
#include <json-c/json.h>

// 创建对象
struct json_object *root = json_object_new_object();

// 添加字段
json_object_object_add(root, "name", json_object_new_string("张三"));
json_object_object_add(root, "age", json_object_new_int(25));

// 添加数组
struct json_object *hobbies = json_object_new_array();
json_object_array_add(hobbies, json_object_new_string("reading"));
json_object_array_add(hobbies, json_object_new_string("swimming"));
json_object_object_add(root, "hobbies", hobbies);

// 打印(格式化输出)
printf("%s\n", json_object_to_json_string_ext(root, JSON_C_TO_STRING_PRETTY));

// 释放内存(引用计数减1,减到0时自动释放)
json_object_put(root);
局限性
  • 编译配置稍复杂:需要编译安装或链接库文件
  • API略显冗长:函数名较长,嵌套调用时代码会比较多
  • 不是最轻量的:二进制体积比cJSON大
适用场景总结
✅ 适合❌ 不适合
Linux/Unix系统级C程序嵌入式/MCU环境
需要长期维护的C项目追求极致轻量的场景
希望有自动内存管理的C项目小型临时工具(太重了)
通过系统包管理器安装的场景

3. jsoncpp —— C++开发者的首选

背景与定位

jsoncpp是最受欢迎的C++ JSON库之一,由多个贡献者共同维护。它的设计理念是让C++开发者能用最自然的方式操作JSON,充分利用C++的语言特性来提供优雅的开发体验。

核心优势

① 符合直觉的运算符重载
这是jsoncpp最吸引人的地方。你可以像操作C++原生容器一样使用[]操作符:

Json::Value root;
root["name"] = "张三";           // 自动创建键值对
root["age"] = 25;                // 自动推断类型
root["hobbies"][0] = "reading";  // 数组自动创建
std::string name = root["name"].asString();  // 类型转换

几乎不需要学习专门的API,C++开发者上手极快。

② 完全自动的内存管理
遵循C++的RAII(资源获取即初始化)原则:

  • 所有的Json::Value对象都像std::string一样自动管理内存
  • 离开作用域时自动析构
  • 开发者无需手动调用任何释放函数,彻底杜绝内存泄漏

③ 丰富的输出格式

// 格式化输出(带缩进,适合人阅读)
std::cout << root.toStyledString() << std::endl;

// 紧凑输出(适合网络传输)
Json::FastWriter writer;
std::string compact = writer.write(root);

// 流式输出(最灵活)
Json::StreamWriterBuilder builder;
builder["indentation"] = "  ";  // 自定义缩进
std::string custom = Json::writeString(builder, root);

④ 完善的错误处理

Json::CharReaderBuilder builder;
JSONCPP_STRING errs;
if (!Json::parseFromStream(builder, input_stream, &root, &errs)) {
    // errs 包含详细的错误信息(行号、错误类型等)
    std::cerr << "JSON解析失败: " << errs << std::endl;
}

⑤ 良好的中文支持

Json::StreamWriterBuilder builder;
builder["emitUTF8"] = true;  // 输出原始UTF-8中文,不转义为\uXXXX
std::string json_with_chinese = Json::writeString(builder, root);
// 输出: {"name":"张三"} 而不是 {"name":"\u5F20\u4E09"}
使用示例
#include <json/json.h>
#include <iostream>
#include <fstream>

int main() {
    // 构建JSON
    Json::Value root;
    root["name"] = "张三";
    root["age"] = 25;
    root["score"] = 98.5;
    root["is_student"] = true;

    // 添加数组
    root["hobbies"].append("reading");
    root["hobbies"].append("coding");

    // 添加嵌套对象
    root["address"]["city"] = "北京";
    root["address"]["street"] = "朝阳路";

    // 序列化输出
    std::cout << "格式化输出:\n" << root.toStyledString() << std::endl;

    // 保存到文件
    std::ofstream file("output.json");
    file << root;
    file.close();

    // 从文件解析
    Json::Value parsed;
    std::ifstream input("output.json");
    input >> parsed;

    std::cout << "读取name: " << parsed["name"].asString() << std::endl;

    return 0;  // 自动释放所有内存
}
局限性
  • 性能不是最快的:比RapidJSON等专门优化的库稍慢
  • 二进制体积较大:比cJSON大不少
  • 需要C++环境:纯C项目无法使用
适用场景总结
✅ 适合❌ 不适合
任何C++项目纯C语言项目
追求开发效率的应用资源极度受限的嵌入式设备
需要快速原型开发追求极致性能的场景
后台服务/客户端软件

三、三者对比总结表

对比维度cJSONjson-cjsoncpp
语言C (ANSI C89)CC++
内存管理手动 (Create/Delete)引用计数 (get/put)自动 (RAII)
文件数量2个多个多个
集成难度⭐ 极低(复制即用)⭐⭐⭐ 中等⭐⭐⭐ 中等
二进制体积⭐ 很小⭐⭐ 中等⭐⭐⭐ 较大
开发效率⭐⭐ 一般⭐⭐ 一般⭐⭐⭐ 很高
内存安全性⭐⭐ 需注意⭐⭐⭐ 较好⭐⭐⭐ 很好
线程安全性非安全需注意需注意
学习成本⭐⭐ 较低⭐⭐⭐ 中等⭐ 最低(C++者)
社区活跃度⭐⭐⭐ 很高⭐⭐ 较高⭐⭐⭐ 很高

四、一句话速记

C++ 开发:用 jsoncpp,省心高效

嵌入式C:用 cJSON,轻量极致

Linux C:用 json-c,稳定可靠

五、补充说明:其他高性能选项

如果项目对性能有极致要求(如高频交易、实时数据处理),或者对现代C++特性(如C++11/14/17)有更高期待,以下两个库也值得了解:

库名语言特点
RapidJSONC++腾讯出品,高性能,Header-only,功能最全面
yyjsonC号称最快的C语言JSON库,单文件,读写达GB/s级别

但在绝大多数常规应用中,前面详细介绍的三个库已经足够胜任。

作者

老丹

关注我
其他文章
上一个

POCO C++ Libraries 详细介绍

下一个

NMEA 0183 协议处理与 C++ 开源库选型指南

关于博主

    老丹是一名C/C++后台开发工程师,信奉“无抽象不设计,无性能不生产”。

  • 技术栈:Modern C++、Linux环境编程、多线程/并发、网络编程等。
  • 信条:能用constexpr解决的问题绝不拖到运行时,能靠RAII避免的泄漏绝不写析构。
  • 正在填坑:从解封装到渲染的C++全链路实现,正在驯服FFmpeg与H.264/H.265。
  • 输出原则:这里的每一段代码都经过-Wall -Wextra -Werror -O2的洗礼。

搜索

近期文章

  • 正向代理与反向代理:一篇搞懂两者的区别 2026年6月6日
  • WP Super Cache 完全指南:为你的 WordPress 博客加速 2026年6月6日
  • WordPress 中 Redis 对象缓存完全指南 2026年6月6日
  • UFW 详解:Linux 防火墙的简洁之道 2026年6月6日
  • NMEA 0183 协议处理与 C++ 开源库选型指南 2026年6月3日

文章分类

  • C/C++开发 (5)
  • Linux服务配置 (3)
  • 计算机理论 (1)
联系我们:📍 地址:中国·广东省深圳市   |   ✉️ 邮箱:support@tanglinux.com   |   💬 QQ:870866607
版权所有:老丹的足迹粤ICP备2026061170号-1       公安备案图标 粤公网安备44030002013274号