cJSON:OpenHarmony 里"无处不在"的微型 JSON 引擎

导读:从开机配置、密钥管理到应用安装、权限控制,OpenHarmony 的各个组件之间交流时使用的"共同语言"就是 JSON。而在 C 语言的世界里,把这种"语言"翻译成程序能直接操作的数据结构,靠的却只是三两个源文件——这就是 cJSON,一个单文件即可落地的超轻量 JSON 解析器。

一、为什么系统内部到处是 JSON

一个操作系统内部有大量"结构化数据交换"的场景:初始化进程要读系统配置、包管理器要解析应用元数据、安全服务要核对权限策略、JS 框架要与原生侧传数据。这些数据用什么格式?JSON 几乎成了默认答案——它可读、可调试、跨语言、标准通用

但在 C 语言里,字符串形式的 JSON 无法直接使用,必须先"解析"成内存中的数据结构,用完后还要"序列化"回字符串。这个反复出现的需求,催生了 cJSON 这样的基础库。

二、cJSON 是什么

cJSON 是 DaveGamble 用纯 ANSI C 编写的一个超轻量 JSON 解析器,采用 MIT 许可,OpenHarmony 引入的版本是 1.7.19

整个库的核心就是两个源文件:

  • cJSON.c —— 实现(约 80KB)
  • cJSON.h —— 头文件与 API 声明

外加一个 cJSON_Utils.c(提供 JSON 补丁、指针等扩展工具)。不依赖任何第三方库,理论上"拷贝即用",这在嵌入式领域是极大的加分项——接入成本几乎为零。

三、灵魂:一个结构体构建的 JSON 树

cJSON 的设计哲学可以用"数据结构极简主义"来形容。整个库的心脏,就是 cJSON.h 里的一个结构体:

typedef struct cJSON {
    struct cJSON *next;       /* 兄弟节点指针(数组元素 / 对象成员) */
    struct cJSON *prev;       /* 前一个兄弟节点 */
    struct cJSON *child;      /* 子节点指针(对象/数组的第一个元素) */
    int type;                 /* 节点类型:对象/数组/字符串/数字/布尔/空 */
    char *valuestring;        /* 字符串值 */
    int valueint;             /* 整数值(历史字段) */
    double valuedouble;       /* 浮点值 */
    char *string;             /* 键名(对象成员时有效) */
} cJSON;

一个结构体 + 链表指针,就同时完成了"解析"和"生成"两种能力:

  • 解析:递归读取 JSON 文本,按层级构建 child 树,兄弟间用 next/prev 串成双向链表;
  • 生成:从树根向下遍历,输出成 JSON 字符串。

理解了这个结构,cJSON 的全部 API 就都"可推断"了——操作对象不外乎是"往树里加节点、从树里取节点、把树打成字符串"。

四、API 全景

4.1 解析(JSON 文本 → cJSON 树)

cJSON *cJSON_Parse(const char *value);                  /* 最常用 */
cJSON *cJSON_ParseWithOpts(const char *value,
                           const char **return_parse_end,
                           cJSON_bool require_null_terminated); /* 精细控制 */
cJSON *cJSON_ParseWithLength(const char *value, size_t buffer_length);
const char *cJSON_GetErrorPtr(void);                    /* 出错位置 */

cJSON_ParseWithOpts 可以拿到解析停止的位置,适合在较大的文本流里定位数据;require_null_terminated 则用于要求整段文本必须合法完整。

4.2 取值(按路径访问)

cJSON *cJSON_GetObjectItem(const cJSON *object, const char *string);  /* 取对象成员 */
cJSON *cJSON_GetArrayItem(const cJSON *array, int index);             /* 取数组元素 */
int cJSON_GetArraySize(const cJSON *array);                           /* 数组长度 */
const char *cJSON_GetStringValue(const cJSON *item);                  /* 取字符串值 */
double cJSON_GetNumberValue(const cJSON *item);                       /* 取数值 */

4.3 构造(程序 → JSON 文本)

cJSON *cJSON_CreateObject(void);
cJSON *cJSON_CreateArray(void);
cJSON *cJSON_CreateString(const char *string);
cJSON *cJSON_CreateNumber(double num);
void cJSON_AddItemToObject(cJSON *object, const char *string, cJSON *item);
void cJSON_AddItemToArray(cJSON *array, cJSON *item);
char *cJSON_Print(const cJSON *item);                 /* 格式化输出 */
char *cJSON_PrintUnformatted(const cJSON *item);      /* 紧凑输出 */
void cJSON_Delete(cJSON *item);                       /* 释放整棵树 */

五、在 OpenHarmony 中的集成

5.1 双形态构建 + 统一配置

BUILD.gn 针对 lite 与 standard 两套构建体系分别产出两种形态:

  • cjson(动态库)
  • cjson_static(静态库)

并为所有形态统一注入防御性配置:

config("cjson_config") {
  include_dirs = [ "//third_party/cJSON" ]
  ldflags = [ "-lm" ]
  defines = [ "CJSON_NESTING_LIMIT=(128)" ]
}

两个细节值得展开:

**(1)-lm**:cJSON 处理数字时依赖 C 标准库的 math.h,因此需要链接数学库;

(2)CJSON_NESTING_LIMIT=(128):这是 cJSON 的递归深度限制。JSON 解析本质是递归下降,恶意构造一个几千层的嵌套 JSON,能让递归栈被打穿(栈溢出),这是嵌入式场景下解析外部数据最常见的攻击面。OpenHarmony 把它统一限制在 128 层,从编译层面堵死了这个隐患。

5.2 认证与部署

在 standard 系统下,cjson 动态库带有 chipsetsdk_spplatformsdk_indirect 等接口标签,并部署到 systemupdater 两个镜像。连"系统升级程序"解析配置都要用它,足以说明 cJSON 在系统里的基础地位。

5.3 部件声明

bundle.json 中部件 cJSON 属于 thirdparty 子系统,适配 mini / small / standard 三档系统,通过 inner_kits 对外提供 cjsoncjson_static 两个库,零组件依赖deps.components 为空)——这也印证了它"轻到没有依赖"的定位。

六、谁在用?——从开机到安装的全链路

在 OpenHarmony 源码中检索依赖 cJSON 的组件,几乎覆盖了系统最核心的几条链路:

  • base/startup/init —— 开机时解析系统配置文件;
  • base/security/huks —— 密钥管理中的策略与属性描述;
  • base/security/appverifybase/security/permission_lite —— 应用签名校验、权限策略解析;
  • foundation/bundlemanager/bundle_framework_litefoundation/ability/appfwk_lite —— 轻量包管理、应用框架的元数据处理;
  • foundation/arkui/ace_engine_lite —— JS 应用与原生侧的数据交换;
  • developtools/syscap_codec —— 系统能力(syscap)编码。

一句话总结:配置文件是它解析的,应用元数据是它解析的,权限策略也是它解析的——cJSON 就是 OpenHarmony 内部信息交换的"事实标准"。

七、上手示例:解析与生成

#include <stdio.h>
#include <stdlib.h>
#include "cJSON.h"

int main(void)
{
    /* ---- 解析 ---- */
    const char *text = "{\"name\":\"player\",\"volume\":80,\"tags\":[\"audio\",\"video\"]}";
    cJSON *root = cJSON_Parse(text);
    if (root == NULL) {
        printf("parse error at: %s\n", cJSON_GetErrorPtr());
        return -1;
    }

    cJSON *name = cJSON_GetObjectItem(root, "name");   /* "player" */
    cJSON *vol  = cJSON_GetObjectItem(root, "volume"); /* 80 */
    cJSON *tags = cJSON_GetObjectItem(root, "tags");
    int  tagCnt = cJSON_GetArraySize(tags);

    printf("name=%s volume=%d tags=%d\n",
           cJSON_GetStringValue(name),
           (int)cJSON_GetNumberValue(vol),
           tagCnt);

    /* ---- 生成 ---- */
    cJSON *out = cJSON_CreateObject();
    cJSON_AddStringToObject(out, "state", "playing");
    cJSON_AddNumberToObject(out, "volume", 80);

    char *buf = cJSON_PrintUnformatted(out);   /* {"state":"playing","volume":80} */
    printf("%s\n", buf);

    free(buf);
    cJSON_Delete(out);
    cJSON_Delete(root);
    return 0;
}

要点:cJSON_Parse 返回的树用完必须 cJSON_Delete 释放;cJSON_PrintUnformatted 返回的字符串由 malloc 分配,需用 free 释放——谁申请谁释放,这是使用 cJSON 最容易踩的坑。

八、质量保障:完善的测试体系

cJSON 自带一套相当完整的单测,覆盖了解析与打印的方方面面:

tests/
├── parse_string.c / parse_array.c / parse_object.c / parse_number.c ...
├── print_string.c / print_array.c / print_object.c / print_value.c ...
├── compare_tests.c          # 节点比较
├── minify_tests.c           # 压缩空白
├── json_patch_tests.c       # JSON Patch
├── parse_with_opts.c        # 精细解析
└── unity/                   # 测试框架

另外还有独立的 fuzzing/ 模糊测试目录。对嵌入式基础库来说,"解析器 + 模糊测试"的组合是安全性的重要保障——它们保证了解析边界情况下的行为可预期,也是 OpenHarmony 引入它时的重要依据。

九、总结

cJSON 用最朴素的代码哲学,解决了最普遍的需求:轻量、零依赖、够用

在 OpenHarmony 里,它早已不只是"一个第三方库",而是贯穿启动、安全、应用框架的数据交换地基。它的意义可以总结为三点:

  1. 接入零成本——单文件、零依赖,任何组件都可以快速引入;
  2. 防御有保障——嵌套深度限制、内存管理约定清晰,配合 fuzzing 测试,可靠性有据可查;
  3. 生态最通用——上游成熟稳定,团队熟悉度高,维护成本低。

对开发者而言,读透这两千来行源码,基本就掌握了整个系统的"配置语言"。

相关源码位置:third_party/cJSONcJSON.c / cJSON.h / cJSON_Utils.c
相关部件:cJSON(子系统 thirdparty

Logo

社区规范:仅讨论OpenHarmony相关问题。

更多推荐