You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用cJSON替换嵌套JSON元素时的异常行为及原因问询

cJSON替换JSON元素时的异常行为解析

场景回顾

用cJSON实现将源JSON的元素覆盖目标JSON的对应元素,两个JSON结构如下:

目标JSON(第一个)

{
    "Data":
    {
        "baud"                  : 9600 ,
        "date"                  : "2022-10-10T13:01",
        "dhcp"                  : false,
        "display"               : 10,
        "ip"                    : "192.168.1.1"
    }
}

源JSON(第二个)

{
    "dhcp": true,
    "ip": "192.168.155.155",
    "date": "1001-01-01T13:01",
    "display": "3",
    "baud": "115200"
}

问题根源分析

1. 部分元素替换失效的原因

cJSON的对象内部是链表结构,cJSON_GetObjectItem系列函数通过遍历链表查找节点。而cJSON_ReplaceItemInObjectCaseSensitive的本质是将源节点从源对象的链表中移除,转移到目标对象的链表中。

你初始代码中,每次替换都直接调用cJSON_GetObjectItem(json_File_Source, key):

  • 前三次替换(baud、date、dhcp)时,源JSON的链表完整,能正常找到节点并转移;
  • 到display时,源JSON的链表已经因为前几次转移操作被破坏,遍历指针无法定位到目标节点,返回NULL导致替换无效;
  • ip同理,源链表残缺后无法正确查找节点,替换操作失效。

提前解析display节点并保存指针,相当于在源链表被修改前就拿到了节点的引用,因此能完成替换。

2. 释放时崩溃的原因

cJSON的节点属于树形结构,每个节点带有parent指针,cJSON_Delete会递归释放整个树的所有节点。当你将源节点转移到目标对象后:

  • 源对象的链表中已无该节点,但内部结构可能残留引用;
  • 先调用cJSON_Delete(json_File_Dest)时,目标对象会递归释放所有包含的节点(包括从源对象转移来的节点);
  • 再调用cJSON_Delete(json_File_Source)时,会尝试释放源对象中已被转移的节点,触发重复释放(double free),导致堆断言错误。

删除一个cJSON_Delete避免了重复释放,因此不会崩溃,但属于治标不治本的临时方案。

正确实现方式

核心解决思路是复制源节点而非直接转移,确保两个JSON对象的内存完全独立:

char* overwrite_JSON(char *source_json_str, char *dest_json_str, char* buffer)
{   
    cJSON *json_source = cJSON_Parse(source_json_str);
    cJSON *json_dest = cJSON_Parse(dest_json_str);
    if (!json_source || !json_dest) {
        if (json_source) cJSON_Delete(json_source);
        if (json_dest) cJSON_Delete(json_dest);
        return NULL;
    }
    cJSON *data_dest = cJSON_GetObjectItemCaseSensitive(json_dest, "Data");
    if (!data_dest) {
        cJSON_Delete(json_source);
        cJSON_Delete(json_dest);
        return NULL;
    }

    // 复制源节点,创建独立副本
    cJSON *baud = cJSON_Duplicate(cJSON_GetObjectItemCaseSensitive(json_source, "baud"), 1);
    cJSON *date = cJSON_Duplicate(cJSON_GetObjectItemCaseSensitive(json_source, "date"), 1);
    cJSON *dhcp = cJSON_Duplicate(cJSON_GetObjectItemCaseSensitive(json_source, "dhcp"), 1);
    cJSON *display = cJSON_Duplicate(cJSON_GetObjectItemCaseSensitive(json_source, "display"), 1);
    cJSON *ip = cJSON_Duplicate(cJSON_GetObjectItemCaseSensitive(json_source, "ip"), 1);

    // 替换目标节点
    cJSON_ReplaceItemInObjectCaseSensitive(data_dest, "baud", baud);
    cJSON_ReplaceItemInObjectCaseSensitive(data_dest, "date", date);
    cJSON_ReplaceItemInObjectCaseSensitive(data_dest, "dhcp", dhcp);
    cJSON_ReplaceItemInObjectCaseSensitive(data_dest, "display", display);
    cJSON_ReplaceItemInObjectCaseSensitive(data_dest, "ip", ip);

    buffer = cJSON_PrintUnformatted(json_dest);
    printf("\n here is my file: %s\n", buffer);

    // 两个JSON对象独立,可安全释放
    cJSON_Delete(json_dest);
    cJSON_Delete(json_source); 

    return buffer;
}

关键注意事项

  • cJSON_ReplaceItemInObjectCaseSensitive默认是转移节点所有权,而非复制,会破坏源对象的结构;
  • 必须用cJSON_Duplicate创建节点副本,确保两个JSON对象的内存互不影响;
  • cJSON_PrintUnformatted返回的是堆内存,调用者需手动用free()释放,避免内存泄漏。

内容的提问来源于stack exchange,提问作者Dansen

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.20 05:30:19