使用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
相关产品推荐
相关产品推荐

