cJSON 适配 OpenHarmony PC 完整指南

项目概述

什么是 cJSON?

cJSON 是一个超轻量级的 JSON 解析库,使用 ANSI C 编写,具有以下特点:

  • 📦 超轻量级:单文件实现,代码简洁高效
  • 🚀 高性能:解析速度快,内存占用小
  • 🔧 易于集成:纯 C 实现,无外部依赖
  • 标准兼容:完全符合 JSON 标准(RFC 7159)
  • 🌐 跨平台:支持多种操作系统和架构

适配版本

  • 目标版本: OpenHarmony-v6.0.0.1-Release
  • cJSON 版本: 1.7.17
  • 架构支持: arm64-v8a
  • 构建系统: CMake
  • 许可证: MIT

核心功能

  • ✅ JSON 解析和生成
  • ✅ 内存管理
  • ✅ UTF-8 支持
  • ✅ 流式解析(可选)
  • ✅ 线程安全(可选)

技术架构

项目结构

cJSON/
├── cJSON.c              # 核心实现文件
├── cJSON.h              # 公共头文件
├── cJSON_Utils.c        # 工具函数(可选)
├── cJSON_Utils.h        # 工具函数头文件
├── CMakeLists.txt       # CMake 构建配置
├── tests/               # 测试套件
└── fuzzing/             # 模糊测试

库类型

cJSON 支持两种库类型:

  1. 静态库 (libcjson.a)

    • 编译时链接
    • 无运行时依赖
    • 适合嵌入式系统
  2. 动态库 (libcjson.so)

    • 运行时链接
    • 节省磁盘空间
    • 支持版本控制

依赖关系

cJSON
└── (无依赖)  # 纯 C 实现,无外部依赖

💡 优势:cJSON 是零依赖库,这使得它在 OpenHarmony PC 上的集成非常简单。


适配过程详解

步骤 1:配置外部仓库

outerrepo/module.json 中添加 cJSON 的配置:

{
    "name": "cJson",
    "branch": "ohos_6.0.0.1",
    "version": "6.0.0.1",
    "type": "git",
    "url": "https://gitcode.com/OpenHarmonyPCDeveloper/ohos_cJson.git"
}

配置说明

  • name: 模块名称,对应目录名(注意大小写:cJson
  • branch: Git 分支名,对应适配版本
  • version: 版本号,用于构建和打包
  • type: 源码获取方式,git 表示使用 Git 克隆
  • url: 源码仓库地址

步骤 2:创建 HPKBUILD 文件

outerrepo/cJson/HPKBUILD 中定义构建流程:

# 包基本信息
pkgname=cJson
pkgver=6.0.0.1
fullpkgver=OpenHarmony-v${pkgver}-Release
pkgrel=0
pkgdesc="Ultralightweight JSON parser in ANSI C."
url="https://gitcode.com/openharmony/third_party_cJSON"
archs=("arm64-v8a")
license=("MIT")

# 依赖声明(cJSON 无依赖)
depends=()
makedepends=()

# 源码配置
source="https://gitcode.com/openharmony/third_party_cJSON.git"
autounpack=false      # 不使用自动解压
downloadpackage=false # 不使用自动下载

builddir=$pkgname-${pkgver}
download_and_patch_flag=true
licensefile=${builddir}/LICENSE

关键配置说明

  1. depends=():

    • cJSON 是零依赖库,不需要任何依赖
    • 这使得构建过程非常简单
  2. autounpack=false:

    • 使用 Git 克隆,不需要自动解压
    • 源码通过 prepare() 函数中的 git clone 获取
  3. license=("MIT"):

    • MIT 许可证,非常宽松
    • 适合商业和开源项目使用

步骤 3:实现 prepare() 函数

prepare() 函数负责准备构建环境:

prepare() {
    # 检查源码目录是否存在
    if [ -d $builddir ]
    then
        echo ${builddir} is already exist
        rm -rf ${builddir}/$ARCH-build
    else
        if [ "$download_and_patch_flag" == true ]
        then
            # Git 克隆源码
            git clone $source $builddir
            if [ $? -ne 0 ]
            then
                return -1
            fi
            
            # 切换到指定版本标签
            pushd $builddir
                git reset --hard $fullpkgver
                if [ $? -ne 0 ]
                then
                    popd
                    return -2
                fi
            popd
            download_and_patch_flag=false
        fi
    fi
    
    # 创建构建目录
    mkdir -p ${builddir}/$ARCH-build

    # 设置交叉编译环境
    if [ $ARCH == "arm64-v8a" ]
    then
        export TARGET_PLATFORM=aarch64-linux-ohos
        setarm64ENV
    else
        echo "${ARCH} not support"
        return -1
    fi
}

关键点

  1. Git 版本控制:

    git clone $source $builddir
    git reset --hard $fullpkgver
    
    • 克隆 OpenHarmony 的 cJSON 适配版本
    • 切换到指定的版本标签(OpenHarmony-v6.0.0.1-Release
  2. 构建目录管理:

    mkdir -p ${builddir}/$ARCH-build
    
    • 为每个架构创建独立的构建目录
    • 支持多架构并行构建
  3. 环境变量设置:

    setarm64ENV
    
    • 设置交叉编译工具链(CC, CXX, AR 等)
    • 设置编译选项(CFLAGS, CXXFLAGS, LDFLAGS)

步骤 4:实现 build() 函数

build() 函数执行实际的编译过程:

build() {
    pushd ${builddir}
        # 配置 CMake
        ${CMAKE} "$@" \
        -DBUILD_SHARED_AND_STATIC_LIBS=ON \
        -DENABLE_CJSON_TEST=OFF \
        -B$ARCH-build -S./ -L

        # 执行编译
        pushd ${ARCH}-build
            ${MAKE} VERBOSE=1 
        ret=$?
        popd
    popd
    return $ret
}

关键点

  1. CMake 配置选项:

    -DBUILD_SHARED_AND_STATIC_LIBS=ON
    
    • 同时构建静态库和动态库
    • 静态库:libcjson.a
    • 动态库:libcjson.so.1.7.17(带版本号)
  2. 禁用测试:

    -DENABLE_CJSON_TEST=OFF
    
    • 构建时不需要运行测试套件
    • 加快构建速度
    • 测试可以在目标设备上进行
  3. CMake 参数:

    • -B$ARCH-build: 指定构建目录
    • -S./: 指定源码目录(当前目录)
    • -L: 列出所有 CMake 变量(调试用)

步骤 5:CMakeLists.txt 配置分析

cJSON 的 CMakeLists.txt 提供了丰富的配置选项:

5.1 编译器标志
option(ENABLE_CUSTOM_COMPILER_FLAGS "Enables custom compiler flags" ON)
if (ENABLE_CUSTOM_COMPILER_FLAGS)
    if (("${CMAKE_C_COMPILER_ID}" STREQUAL "Clang") OR ("${CMAKE_C_COMPILER_ID}" STREQUAL "GNU"))
        list(APPEND custom_compiler_flags
            -std=c89
            -pedantic
            -Wall
            -Wextra
            -Werror
            # ... 更多警告选项
        )
    endif()
endif()

说明

  • 使用 C89 标准,确保最大兼容性
  • 启用严格的警告检查
  • 使用 -Werror 将警告视为错误
5.2 库类型配置
option(BUILD_SHARED_AND_STATIC_LIBS "Build both shared and static libraries" OFF)

选项说明

  • ON: 同时构建静态库和动态库(本适配使用)
  • OFF: 只构建静态库或动态库(取决于 BUILD_SHARED_LIBS
5.3 符号可见性
option(ENABLE_PUBLIC_SYMBOLS "Export library symbols." On)
if (ENABLE_PUBLIC_SYMBOLS)
    list(APPEND custom_compiler_flags -fvisibility=hidden)
    add_definitions(-DCJSON_EXPORT_SYMBOLS -DCJSON_API_VISIBILITY)
endif()

说明

  • 控制符号的可见性
  • 减少符号冲突
  • 提高库的安全性

步骤 6:实现 package() 函数

package() 函数安装编译产物:

package() {
    pushd ${builddir}/$ARCH-build
        $MAKE VERBOSE=1 install PREFIX=$LYCIUM_ROOT/usr/$pkgname/$ARCH >> $buildlog 2>&1
        ret=$?
    popd
    return $ret
}

安装内容

安装后的目录结构:

lycium/usr/cJson/arm64-v8a/
├── include/
│   └── cjson/
│       └── cJSON.h          # 公共头文件
├── lib/
│   ├── libcjson.a           # 静态库
│   ├── libcjson.so          # 动态库符号链接
│   ├── libcjson.so.1        # 版本符号链接
│   ├── libcjson.so.1.7.17   # 实际动态库文件
│   ├── cmake/
│   │   └── cJSON/
│   │       ├── cJSONConfig.cmake
│   │       ├── cJSONConfigVersion.cmake
│   │       ├── cjson-release.cmake
│   │       └── cjson.cmake
│   └── pkgconfig/
│       └── libcjson.pc      # pkg-config 配置文件
└── hnp.json                 # HNP 配置文件

文件说明

文件/目录 说明
include/cjson/cJSON.h 公共头文件,应用程序需要包含
lib/libcjson.a 静态库,编译时链接
lib/libcjson.so.* 动态库,运行时链接
lib/cmake/cJSON/ CMake 配置文件,用于 CMake 项目集成
lib/pkgconfig/libcjson.pc pkg-config 配置文件,用于查找库

步骤 7:实现 archive() 函数

archive() 函数生成最终的打包产物:

archive() {
    # 创建输出目录
    mkdir -p ${LYCIUM_ROOT}/output/$ARCH
    
    # 创建 tar.gz 归档
    pushd $LYCIUM_ROOT/usr/$pkgname/$ARCH
        tar -zvcf ${LYCIUM_ROOT}/output/$ARCH/${pkgname}_${pkgver}.tar.gz *
    popd
    
    # 复制 HNP 配置文件
    cp hnp.json $LYCIUM_ROOT/usr/$pkgname/$ARCH
    
    # 生成 HNP 包
    ${HNP_TOOL} pack -i ${LYCIUM_ROOT}/usr/$pkgname/$ARCH -o ${LYCIUM_ROOT}/output/$ARCH/
}

说明

  • 生成两种格式的包:
    1. tar.gz: 二进制归档包,包含所有库文件和头文件
    2. .hnp: HarmonyOS Native Package,用于 HarmonyOS 系统安装

步骤 8:配置 hnp.json

hnp.json 定义 HNP 包的元数据:

{
    "type": "hnp-config",
    "name": "cJson",
    "version": "1.7.17",
    "install": {}
}

字段说明

  • type: 固定为 "hnp-config"
  • name: 包名称,必须与 pkgname 一致(注意大小写)
  • version: cJSON 的实际版本号(1.7.17),不是适配版本号
  • install: 安装规则(空对象表示安装所有文件)

⚠️ 注意version 字段使用的是 cJSON 的实际版本号(1.7.17),而不是适配版本号(6.0.0.1)。


构建流程

完整构建命令

cd lycium
./build.sh cJson

构建过程详解

开始构建

读取 module.json

Git 克隆源码

切换到指定版本

准备构建环境

配置 CMake

编译静态库和动态库

安装到usr目录

生成tar.gz

生成hnp包

完成

构建日志示例

Build OS Darwin
OHOS_SDK=/Users/jianguo/Library/OpenHarmony/Sdk/20
CLANG_VERSION=15.0.4
Start building cJson 6.0.0.1!
Compileing OpenHarmony arm64-v8a cJson 6.0.0.1 libs...
cJson-6.0.0.1 is already exist
-- The C compiler identification is Clang 15.0.4
-- The CXX compiler identification is Clang 15.0.4
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Check for working C compiler: /Users/jianguo/Library/OpenHarmony/Sdk/20/native/llvm/bin/aarch64-linux-ohos-clang - skipped
-- Detecting C compile features
-- Detecting C compile features - done
-- Configuring done
-- Generating done
-- Build files have been written to: arm64-v8a-build
[ 50%] Building C object CMakeFiles/cjson.dir/cJSON.c.o
[100%] Linking C shared library libcjson.so.1.7.17
[100%] Built target cjson
[ 50%] Building C object CMakeFiles/cjson-static.dir/cJSON.c.o
[100%] Linking C static library libcjson.a
[100%] Built target cjson-static
Install the project...
-- Installing: /Users/jianguo/Desktop/harmony/tpc/lycium_plusplus/lycium/usr/cJson/arm64-v8a/include/cjson/cJSON.h
-- Installing: /Users/jianguo/Desktop/harmony/tpc/lycium_plusplus/lycium/usr/cJson/arm64-v8a/lib/libcjson.so.1.7.17
-- Installing: /Users/jianguo/Desktop/harmony/tpc/lycium_plusplus/lycium/usr/cJson/arm64-v8a/lib/libcjson.so.1
-- Installing: /Users/jianguo/Desktop/harmony/tpc/lycium_plusplus/lycium/usr/cJson/arm64-v8a/lib/libcjson.so
-- Installing: /Users/jianguo/Desktop/harmony/tpc/lycium_plusplus/lycium/usr/cJson/arm64-v8a/lib/libcjson.a
-- Installing: /Users/jianguo/Desktop/harmony/tpc/lycium_plusplus/lycium/usr/cJson/arm64-v8a/lib/cmake/cJSON/cJSONConfig.cmake
-- Installing: /Users/jianguo/Desktop/harmony/tpc/lycium_plusplus/lycium/usr/cJson/arm64-v8a/lib/pkgconfig/libcjson.pc
Build cJson 6.0.0.1 end!
ALL JOBS DONE!!!

构建产物

目录结构

lycium/
├── output/
│   └── arm64-v8a/
│       ├── cJson.hnp              # HNP 归档包
│       └── cJson_6.0.0.1.tar.gz  # 二进制归档包
└── usr/
    └── cJson/
        └── arm64-v8a/
            ├── include/
            │   └── cjson/
            │       └── cJSON.h
            ├── lib/
            │   ├── libcjson.a
            │   ├── libcjson.so -> libcjson.so.1
            │   ├── libcjson.so.1 -> libcjson.so.1.7.17
            │   ├── libcjson.so.1.7.17
            │   ├── cmake/
            │   │   └── cJSON/
            │   │       ├── cJSONConfig.cmake
            │   │       ├── cJSONConfigVersion.cmake
            │   │       ├── cjson-release.cmake
            │   │       └── cjson.cmake
            │   └── pkgconfig/
            │       └── libcjson.pc
            └── hnp.json

产物说明

1. 头文件
$ file lycium/usr/cJson/arm64-v8a/include/cjson/cJSON.h
cJSON.h: ASCII text

用途

  • 应用程序需要包含此头文件
  • 包含所有 cJSON API 定义
2. 静态库
$ file lycium/usr/cJson/arm64-v8a/lib/libcjson.a
libcjson.a: current ar archive

特点

  • 编译时链接到应用程序
  • 无运行时依赖
  • 适合嵌入式系统
3. 动态库
$ file lycium/usr/cJson/arm64-v8a/lib/libcjson.so.1.7.17
libcjson.so.1.7.17: ELF 64-bit LSB shared object, ARM aarch64, version 1 (SYSV), dynamically linked, interpreter /lib/ld-musl-aarch64.so.1, with debug_info, not stripped

特点

  • ARM64 架构
  • 动态链接到 musl C 库
  • 包含版本号(1.7.17)
  • 包含调试信息
4. CMake 配置文件

用途

  • 用于 CMake 项目集成
  • 自动查找库和头文件
  • 设置编译选项

使用示例

find_package(cJSON REQUIRED)
target_link_libraries(myapp cjson)
5. pkg-config 配置文件

用途

  • 用于 pkg-config 工具查找库
  • 自动设置编译和链接选项

使用示例

pkg-config --cflags --libs libcjson

使用示例

示例 1:基本 JSON 解析

#include <cjson/cJSON.h>
#include <stdio.h>
#include <stdlib.h>

int main() {
    // JSON 字符串
    const char *json_string = "{\"name\":\"cJSON\",\"version\":\"1.7.17\"}";
    
    // 解析 JSON
    cJSON *json = cJSON_Parse(json_string);
    if (json == NULL) {
        const char *error_ptr = cJSON_GetErrorPtr();
        if (error_ptr != NULL) {
            fprintf(stderr, "Error before: %s\n", error_ptr);
        }
        return 1;
    }
    
    // 获取字段值
    cJSON *name = cJSON_GetObjectItemCaseSensitive(json, "name");
    cJSON *version = cJSON_GetObjectItemCaseSensitive(json, "version");
    
    if (cJSON_IsString(name) && (name->valuestring != NULL)) {
        printf("Name: %s\n", name->valuestring);
    }
    if (cJSON_IsString(version) && (version->valuestring != NULL)) {
        printf("Version: %s\n", version->valuestring);
    }
    
    // 释放内存
    cJSON_Delete(json);
    
    return 0;
}

编译命令

aarch64-linux-ohos-clang \
    -I$LYCIUM_ROOT/usr/cJson/arm64-v8a/include \
    -L$LYCIUM_ROOT/usr/cJson/arm64-v8a/lib \
    -lcjson \
    example.c -o example

示例 2:创建 JSON 对象

#include <cjson/cJSON.h>
#include <stdio.h>

int main() {
    // 创建 JSON 对象
    cJSON *json = cJSON_CreateObject();
    cJSON *name = cJSON_CreateString("cJSON");
    cJSON *version = cJSON_CreateString("1.7.17");
    cJSON *features = cJSON_CreateArray();
    
    cJSON_AddItemToObject(json, "name", name);
    cJSON_AddItemToObject(json, "version", version);
    cJSON_AddItemToObject(json, "features", features);
    
    // 添加数组元素
    cJSON_AddItemToArray(features, cJSON_CreateString("lightweight"));
    cJSON_AddItemToArray(features, cJSON_CreateString("portable"));
    cJSON_AddItemToArray(features, cJSON_CreateString("standard"));
    
    // 转换为字符串
    char *json_string = cJSON_Print(json);
    printf("%s\n", json_string);
    
    // 释放内存
    free(json_string);
    cJSON_Delete(json);
    
    return 0;
}

示例 3:使用 CMake 集成

CMakeLists.txt

cmake_minimum_required(VERSION 3.10)
project(myapp)

set(CMAKE_C_STANDARD 11)

# 查找 cJSON
find_path(CJSON_INCLUDE_DIR cjson/cJSON.h
    PATHS ${LYCIUM_ROOT}/usr/cJson/arm64-v8a/include)

find_library(CJSON_LIBRARY
    NAMES cjson
    PATHS ${LYCIUM_ROOT}/usr/cJson/arm64-v8a/lib)

# 添加可执行文件
add_executable(myapp main.c)

# 链接库
target_include_directories(myapp PRIVATE ${CJSON_INCLUDE_DIR})
target_link_libraries(myapp ${CJSON_LIBRARY})

关键技术点

1. 零依赖设计

优势

  • ✅ 集成简单,无需处理依赖关系
  • ✅ 编译快速,减少构建时间
  • ✅ 部署方便,无运行时依赖
  • ✅ 兼容性好,适合各种环境

实现

  • 纯 ANSI C 实现
  • 只依赖标准 C 库
  • 无第三方库依赖

2. 双库支持

静态库 (libcjson.a):

  • 编译时链接
  • 适合嵌入式系统
  • 无运行时依赖

动态库 (libcjson.so):

  • 运行时链接
  • 节省磁盘空间
  • 支持版本控制

配置

-DBUILD_SHARED_AND_STATIC_LIBS=ON

3. CMake 集成支持

配置文件

  • cJSONConfig.cmake: CMake 包配置
  • cJSONConfigVersion.cmake: 版本配置
  • cjson.cmake: 导入目标定义

使用方式

find_package(cJSON REQUIRED)
target_link_libraries(myapp cjson)

4. pkg-config 支持

配置文件lib/pkgconfig/libcjson.pc

使用方式

pkg-config --cflags --libs libcjson

输出示例

-I/usr/include/cjson -L/usr/lib -lcjson

常见问题与解决方案

问题 1:Git 克隆失败

症状

git clone $source $builddir
ERROR: Failed to clone repository

解决方案

  1. 检查网络连接
  2. 验证仓库 URL
  3. 检查 Git 凭据
# 手动测试
git clone https://gitcode.com/openharmony/third_party_cJSON.git test

问题 2:版本标签不存在

症状

git reset --hard OpenHarmony-v6.0.0.1-Release
fatal: Could not resolve HEAD to a revision

解决方案

  1. 检查标签是否存在
  2. 使用正确的标签格式
# 列出所有标签
git tag -l

# 检查特定标签
git show-ref --tags | grep 6.0.0.1

问题 3:CMake 配置失败

症状

CMake Error: Could not find CMAKE_C_COMPILER

解决方案

  1. 确保环境变量已设置
  2. 检查工具链路径
# 检查环境变量
echo $CC
echo $CMAKE

# 验证工具链
which aarch64-linux-ohos-clang

问题 4:链接错误

症状

undefined reference to `cJSON_Parse'

解决方案

  1. 确保链接了 cJSON 库
  2. 检查库路径
# 检查库文件
ls -lh lycium/usr/cJson/arm64-v8a/lib/

# 验证链接选项
aarch64-linux-ohos-clang ... -L/path/to/lib -lcjson

问题 5:头文件找不到

症状

fatal error: 'cjson/cJSON.h' file not found

解决方案

  1. 检查头文件路径
  2. 添加包含目录
# 检查头文件
ls -lh lycium/usr/cJson/arm64-v8a/include/cjson/

# 添加包含选项
-I$LYCIUM_ROOT/usr/cJson/arm64-v8a/include

最佳实践

1. 内存管理

  • ✅ 使用 cJSON_Delete() 释放 JSON 对象
  • ✅ 使用 free() 释放 cJSON_Print() 返回的字符串
  • ✅ 检查返回值,避免空指针访问
  • ✅ 使用 cJSON_GetErrorPtr() 获取错误信息

2. 错误处理

cJSON *json = cJSON_Parse(json_string);
if (json == NULL) {
    const char *error_ptr = cJSON_GetErrorPtr();
    if (error_ptr != NULL) {
        fprintf(stderr, "Error: %s\n", error_ptr);
    }
    return 1;
}

3. 类型检查

cJSON *item = cJSON_GetObjectItem(json, "key");
if (cJSON_IsString(item) && (item->valuestring != NULL)) {
    // 安全使用字符串值
    printf("%s\n", item->valuestring);
}

4. 性能优化

  • ✅ 使用 cJSON_ParseWithOpts() 进行流式解析
  • ✅ 避免频繁创建和删除 JSON 对象
  • ✅ 使用对象池管理 JSON 对象
  • ✅ 合理使用 cJSON_Minify() 压缩 JSON

总结

适配要点

  1. 零依赖优势:cJSON 无外部依赖,集成简单
  2. 双库支持:同时提供静态库和动态库
  3. 标准兼容:完全符合 JSON 标准
  4. 易于使用:API 简洁,文档完善
  5. 高性能:解析速度快,内存占用小

技术亮点

  • ✅ 纯 C 实现,跨平台兼容
  • ✅ CMake 和 pkg-config 支持
  • ✅ 完善的错误处理机制
  • ✅ 丰富的 API 接口

适用场景

  • JSON 数据解析和生成
  • 配置文件处理
  • API 数据交换
  • 日志格式化
  • 数据序列化

关键词: cJSON, OpenHarmony, JSON解析, CMake, 零依赖, 静态库, 动态库, Lycium

欢迎大家加入鸿蒙pc开发者社区

Logo

开源鸿蒙跨平台开发社区汇聚开发者与厂商,共建“一次开发,多端部署”的开源生态,致力于降低跨端开发门槛,推动万物智联创新。

更多推荐