当前位置:首页 > 公众号精选 > 码农爱学习

本篇来介绍如何将json数据写入文件,并支持对json文件中的字段进行修改。

1 cJSON

本篇使用cJSON来演示,cJSON 是一个轻量级的 C 语言 JSON 解析库,API 简洁易用,核心功能围绕 JSON 的创建、解析、修改和序列化展开。

只需要引入两个文件:

  • cJSON.c
  • cJSON.h

使用cJSON来操作 json数据,需要先了解cJSON的API接口。

1.1 cJSON常用API

1.1.1 json对象 / 数组的创建

API 函数功能说明
cJSON *cJSON_CreateObject(void)创建一个空的 JSON 对象({}),返回指向该对象的指针
cJSON *cJSON_CreateArray(void)创建一个空的 JSON 数组([]),返回指向该数组的指针
cJSON *cJSON_CreateString(const char *string)创建一个字符串类型的 JSON 元素(如 "name": "张三" 中的值)
cJSON *cJSON_CreateNumber(double num)创建一个数字类型的 JSON 元素(支持整数、浮点数)
cJSON *cJSON_CreateBool(cJSON_bool boolean)创建一个布尔类型的 JSON 元素(true 或 false,参数为 1 或 0)
cJSON *cJSON_CreateNull(void)创建一个 null 类型的 JSON 元素

1.1.2 向对象 / 数组添加元素

API 函数功能说明
void cJSON_AddItemToObject(cJSON *object, const char *string, cJSON *item)向 JSON 对象添加一个键值对(string 为键,item 为值)
void cJSON_AddItemToArray(cJSON *array, cJSON *item)向 JSON 数组添加一个元素(无需键,按顺序排列)
cJSON *cJSON_AddStringToObject(cJSON *object, const char *name, const char *string)****向对象添加字符串类型的键值对(内部自动创建字符串元素)
cJSON *cJSON_AddNumberToObject(cJSON *object, const char *name, double number)向对象添加数字类型的键值对
cJSON *cJSON_AddBoolToObject(cJSON *object, const char *name, cJSON_bool boolean)向对象添加布尔类型的键值对

1.1.3 JSON 解析(从字符串到对象)

API 函数功能说明
cJSON *cJSON_Parse(const char *value)解析 JSON 字符串,返回根对象 / 数组指针。若解析失败,返回 NULL
const char *cJSON_GetErrorPtr(void)当 cJSON_Parse 失败时,调用此函数获取错误位置的描述(辅助调试)

1.1.4 读取 JSON 元素(获取字段值)

API 函数功能说明
cJSON *cJSON_GetObjectItem(const cJSON *object, const char *string)从 JSON 对象中获取指定键对应的元素(返回元素指针,若不存在则为 NULL)
cJSON *cJSON_GetArrayItem(const cJSON *array, int index)从 JSON 数组中获取指定索引(index)的元素(索引从 0 开始)
int cJSON_GetArraySize(const cJSON *array)获取 JSON 数组的长度(元素个数)
const char *cJSON_GetStringValue(const cJSON *item)从字符串类型的元素中获取字符串值(需先确认元素类型为 cJSON_String)
double cJSON_GetNumberValue(const cJSON *item)从数字类型的元素中获取数值(需先确认元素类型为 cJSON_Number)
cJSON_bool cJSON_IsBool(const cJSON *item)判断元素是否为布尔类型(返回 1 是,0 否)
cJSON_bool cJSON_IsNull(const cJSON *item)判断元素是否为 null 类型

1.1.5 修改 JSON 元素

API 函数功能说明
void cJSON_SetValuestring(cJSON *item, const char *string)将元素的值修改为字符串(需元素原本为字符串类型,或强制覆盖)
void cJSON_SetNumberValue(cJSON *item, double number)将元素的值修改为数字
void cJSON_SetBoolValue(cJSON *item, cJSON_bool boolean)将元素的值修改为布尔值

1.1.6 JSON 序列化(从对象到字符串)

API 函数功能说明
char *cJSON_Print(const cJSON *item)将 JSON 对象 / 数组转换为带缩进的格式化字符串(可读性好,体积较大)
char *cJSON_PrintUnformatted(const cJSON *item)转换为无缩进的紧凑字符串(体积小,适合传输)
char *cJSON_PrintBuffered(const cJSON *item, int prebuffer, cJSON_bool fmt)带缓冲的序列化,prebuffer 为预分配缓冲区大小,fmt 为 1 时格式化

1.1.7 内存释放

API 函数功能说明
void cJSON_Delete(cJSON *item)递归释放一个 JSON 对象 / 数组及其所有子元素占用的内存(根对象释放后,整个结构都被释放)

1.1.8 其它

API 函数功能说明
cJSON *cJSON_Duplicate(const cJSON *item, cJSON_bool recurse)复制一个 JSON 元素(recurse=1 时递归复制子元素)。
void cJSON_ReplaceItemInObject(cJSON *object, const char *string, cJSON *newitem)替换对象中指定键的元素(旧元素会被自动释放)。
void cJSON_ReplaceItemInArray(cJSON *array, int index, cJSON *newitem)替换数组中指定索引的元素。

1.2 创建json示例

这里示例创建一个json对象,并添加一些不同类型的字段:

  • cJSON_CreateObject:创建json对象
  • cJSON_AddStringToObject:添加字符串类型的数据
  • cJSON_AddNumberToObject:添加数值类型的数据
  • cJSON_AddBoolToObject:添加BOOL类型的数据
int test_create_a_json_object(char **json_str)
{
    // 创建JSON
    cJSON *root = cJSON_CreateObject();
    if (root == NULL) 
    {
        PRINT("cJSON_CreateObject err!\n");
        return-1;
    }

    // 向根对象添加字段(支持字符串、数字、布尔等类型)
    cJSON_AddStringToObject(root, "name", "张三");    // 字符串字段
    cJSON_AddNumberToObject(root, "age", 22);        // 数字字段
    cJSON_AddBoolToObject(root, "is_student", 1);    // 布尔字段(0=false,1=true)
    cJSON_AddStringToObject(root, "major", "计算机科学"); // 待修改的示例字段

    // 将JSON对象转换为格式化字符串(带缩进,便于阅读)
    *json_str = cJSON_Print(root);
    if (json_str == NULL) 
    {
        PRINT("cJSON_Print err!\n");
        cJSON_Delete(root); // 释放JSON对象内存,避免泄漏
        return-1;
    }
    PRINT("json_str:%s\n", *json_str);
    
    cJSON_Delete(root);
    return0;
}

运行结果

[test_create_a_json_object][212] json_str:{
 "name": "张三",
 "age": 22,
 "is_student": true,
 "major": "计算机科学"
}

2 文件操作

创建json数据后,如果想将其写入文件,可以先将json数据转换为字符串(cJSON_Print),然后再将其整个写入文件。

2.1 写入文件

// 将JSON数据写入文件
int json_write_to_file(const char *filename, char *json_str) 
{
    if (NULL == filename || NULL == json_str)
    {
        PRINT("NULL ptr\n");
        return-1;
    }
    
    // 打开文件("w"模式:覆盖写入,若文件不存在则创建)
    FILE *fp = fopen(filename, "w");
    if (fp == NULL) 
    {
        PRINT("fopen %s err!\n", filename);
        return-1;
    }

    // 写入JSON字符串到文件
    fprintf(fp, "%s", json_str);

    // 释放资源
    fclose(fp);
    return0;
}

2.2 修改json文件中的值

对于写入文件中的json数据,如果想要修改json中的数据,思路如下:

  • 先将json数据从文件中整个读出来
  • 然后对json数据进行解析(cJSON_Parse)
  • 接着查找到要修改的字段,进行修改
  • 最后再将整个json数据写回文件

对文件的读写的操作如下,函数的参数:

  • const char *filename:要操作的json文件的指针
  • const char *field_name:要修改的字段名称
  • const char *new_value:要修改为的值
// 修改文件中JSON的指定字段(支持字符串/数字/布尔类型)
int jsonfile_modify_field(const char *filename, const char *field_name, const char *new_value) 
{
    // 打开文件
    FILE *fp = fopen(filename, "r");
    if (fp == NULL) 
    {
        PRINT("fopen %s err!\n", filename);
        return-1;
    }

    // 获取文件长度(用于分配缓冲区)
    fseek(fp, 0, SEEK_END);
    long file_len = ftell(fp);
    fseek(fp, 0, SEEK_SET);

    // 分配缓冲区(+1 用于存储字符串结束符 '\0')
    char *buffer = (char *)malloc(file_len + 1);
    if (buffer == NULL) 
    {
        PRINT("malloc err!\n");
        fclose(fp);
        return-1;
    }

    // 读取文件内容到缓冲区
    fread(buffer, 1, file_len, fp);
    buffer[file_len] = '\0'; // 手动添加字符串结束符
    fclose(fp);

    // 解析缓冲区中的JSON字符串
    cJSON *root = cJSON_Parse(buffer);
    free(buffer); // 缓冲区已用完,释放内存
    if (root == NULL) 
    {
        PRINT("cJSON_Parse err!\n");
        return-1;
    }
    
    // 修改JSON字段
    if (0 != json_modify_field(root, field_name, new_value))
    {
        PRINT("json_modify_field err!\n");
        cJSON_Delete(root);
        return-1;
    }

    // 将修改后的JSON重新写入文件(覆盖原内容)
    char *modified_json = cJSON_Print(root);
    if (modified_json == NULL) 
    {
        PRINT("modify err!\n");
        cJSON_Delete(root);
        return-1;
    }

    // 以"w"模式打开文件(覆盖原内容)
    fp = fopen(filename, "w");
    if (fp == NULL) 
    {
        PRINT("fopen %s err!\n", filename);
        free(modified_json);
        cJSON_Delete(root);
        return-1;
    }

    fprintf(fp, "%s", modified_json);
    PRINT("modify [%s] -> [%s] ok,now:\n%s\n", field_name, new_value, modified_json);

    // 释放所有资源
    fclose(fp);
    free(modified_json);
    cJSON_Delete(root);
    return0;
}

具体的对json字段进行修改的逻辑,单独封装为json_modify_field

函数的参数:

  • cJSON *root:要操作的json指针
  • const char *field_name:要修改的字段名称
  • const char *new_value:要修改为的值

这里用到的API接口

  • cJSON_GetObjectItem:从 JSON 对象中获取指定键对应的元素,另外通过type还可判断字段的类型
  • 对字段的值进行修改:
    • cJSON_SetValuestring
    • cJSON_SetNumberValue
    • cJSON_SetBoolValue
// 修改JSON的指定字段(支持字符串/数字/布尔类型)
int json_modify_field(cJSON *root, const char *field_name, const char *new_value) 
{
    // 查找指定字段,若存在则修改;若不存在则提示
    cJSON *target_field = cJSON_GetObjectItem(root, field_name);
    if (target_field == NULL) 
    {
        PRINT("no find:%s\n", field_name);
        return-1;
    }

    // 获取原字段类型并修改
    int modify_ok = 0;
    switch (target_field->type) 
    {
        // 原字段是字符串:直接用新值替换
        case cJSON_String:
        {
            cJSON_SetValuestring(target_field, new_value);
            modify_ok = 1;
            break;
        }
        // 原字段是数字:尝试将 new_value 转为数字
        case cJSON_Number:
        {
            char *endptr;
            double num_val = strtod(new_value, &endptr);
            // 检查转换是否成功(new_value 必须全是数字)
            if (*endptr == '\0') 
            { 
                // 没有多余字符,转换有效
                cJSON_SetNumberValue(target_field, num_val);
                modify_ok = 1;
            } 
            else
            {
                PRINT("new value [%s] convert to num err\n", new_value);
            }
            break;
        }
        // 原字段是布尔值:将 new_value 转为 0(false)或 1(true)
        case cJSON_True:
        case cJSON_False:
        {
            // 支持 "0"/"false" 转为 false;"1"/"true" 转为 true(不区分大小写)
            if (strcmp(new_value, "0") == 0 || strcasecmp(new_value, "false") == 0) 
            {
                cJSON_SetBoolValue(target_field, 0);
                modify_ok = 1;
            } 
            elseif (strcmp(new_value, "1") == 0 || strcasecmp(new_value, "true") == 0) 
            {
                cJSON_SetBoolValue(target_field, 1);
                modify_ok = 1;
            } 
            else
            {
                PRINT("new value [%s] convert to bool err\n", new_value);
            }
            break;
        }
        default:
        {
            // 不支持的类型(如数组、对象等)
            PRINT("err type: %d\n", target_field->type);
            break;
        }
    }

    if (!modify_ok) 
    {
        return-1;
    }
    
    return0;
}

3 整体测试

下面是整体测试的主函数,基本流程为:

  • 创建json数据
  • 写入文件
  • 依次修改josn文件中不同类型的数据

在修改后,对文件的josn数据进行打印

// gcc test1.c cjson/cJSON.c -o test1 -lm
#include 
#include 
#include 
#include 
#include "cjson/cJSON.h"

#define PRINT(fmt, ...) printf("[%s][%d] " fmt, __func__, __LINE__, ##__VA_ARGS__)

int main() 
{
    constchar *filename = "user_info.json"; // 目标JSON文件
    PRINT("filename:%s\n", filename);

    char *json_str = NULL;
    if (0 != test_create_a_json_object(&json_str))
    {
        PRINT("test_create_a_json_object err!\n");
        return-1;
    }
    
    // 将JSON写入文件
    if (json_write_to_file(filename, json_str) != 0) 
    {
        PRINT("json_write_to_file err!\n");
        return-1;
    }
    free(json_str);
    
    // 修改指定字段("age" 为 25)
    jsonfile_modify_field(filename, "age", "25");
    
    // 修改指定字段("is_srudent" 为 false)
    jsonfile_modify_field(filename, "is_student", "false");

    // 修改指定字段("major" 为 "嵌入式软件")
    jsonfile_modify_field(filename, "major", "嵌入式软件");

    return0;
}

实际运行结果如下:

4 总结

本篇介绍了如何使用cJSON库以及借助文件操作,将json数据写入文件,并支持对文件中的json数据进行按字段的指定修改。文章推荐

cJSON基础介绍与代码测试JSON Schema基础入门【C文件操作1】如何写入读取?fopen的6种组合参数怎么用?【C文件操作2】如何随机的进行文件读取?pyQt实现一种按钮切换关联变化的勾选框的逻辑

本站声明: 本文章由作者或相关机构授权发布,目的在于传递更多信息,并不代表本站赞同其观点,本站亦不保证或承诺内容真实性等。需要转载请联系该专栏作者,如若文章内容侵犯您的权益,请及时联系本站删除( 邮箱:macysun@21ic.com )。
关闭