从Json对象中提取某个对象的一点注意--libjson-c

1,引入

最近使用libjson-c库开发一些简单的json功能,我们知道libjson-c的内存管理是引用计数的方式,当我们put时,引用计数-1,当引用计数为0 时,内存被释放,使用当我们从一个json对象中解析或换回一个对象时,我们应该要关心内存安全(内存泄漏,多重释放等)问题。

2, 下面将提供一些方式:

用于测试的json字符串:这里我们只关心data数组中的每个对象元素

cpp 复制代码
 {
	 "type":"perf",
	 "data": [
		{
			"apples": {
				"size":"512",
				"id":"2026"
			}
		},
		{
			"xiaomis": {
				"size":"256",
				"id":"2025"					
			}
		},
		{
			"oppos":{
				"size":"128",
				"id":"2024"
			}
		}
	 ]
 }

初始化时的根对象:

//将字符串解析为一个json对象,此时内部使用键值对引用计数为1(这里键值对特指'值')

root = json_tokener_parse(json_str);

3,json解析函数

方法1: 创建新的对象并返回 (推荐使用)

从原始的json对象中提前关注的json对象到新的对象中,注意:这里如果我们不在关心root对象中其他的键值对,

最好对需要关注的键值对 <增加引用计数 json_object_get> ,防止内存溢出,或多重释放导致的段错误,

这样我们在使用json_object_put释放root的时候,我们关心的对象将不会被释放,即我们任然可以使用result指针(返回值)访问

cpp 复制代码
struct json_object* extract_inner_objects(const char* json_str) 
{
	int i = 0;
	struct json_object* root = NULL;
	struct json_object* result = NULL;
    struct json_object* data_array = NULL;
	
	// 解析JSON字符串
	root = json_tokener_parse(json_str);
	if (!root) {
		printf("json parase is failed.\n");
		return NULL;
	}
	
	// 获取data数组
    if (!json_object_object_get_ex(root, "data", &data_array) || 
        !json_object_is_type(data_array, json_type_array)) 
	{
        json_object_put(root);
		printf("get json 'data' is failed.\n");
        return NULL;
    }
	
	result = json_object_new_object();
    if (!result) {
        json_object_put(root);// 释放原始JSON对象
		printf("new json 'result' is failed.\n");
		return NULL;
	}
	
	int array_len = json_object_array_length(data_array);
	for (i = 0; i < array_len; i++) {
		struct json_object* item = json_object_array_get_idx(data_array, i);
        // 遍历item中的所有键值对
        json_object_object_foreach(item, key, val) {
            if (json_object_is_type(val, json_type_object)) {
                // 将最内层对象添加到结果中
                json_object_object_add(result, key, val);
                // 增加引用计数,防止val被释放
                json_object_get(val);
            }
        }
	}
	
    // 释放原始JSON对象
    json_object_put(root);
	
    return result;
}

方法2:不增加引用计数,当释放时,只能释放最原始的root对象,而不能释放result对象

cpp 复制代码
struct json_object* extract_inner_objects2(const char* json_str, struct json_object** result) 
{	
	int i = 0;
	struct json_object* root = NULL;
    struct json_object* data_array = NULL;
	
	if (!result) {
		printf("'result' is not NULL.\n");
		return NULL;
	}
	
	// 解析JSON字符串
	root = json_tokener_parse(json_str);
	if (!root) {
		printf("json parase is failed.\n");
	}	
	
	// 获取data数组
    if (!json_object_object_get_ex(root, "data", &data_array) || 
        !json_object_is_type(data_array, json_type_array)) 
	{
        json_object_put(root);
		printf("get json 'data' is failed.\n");
        return NULL;
    }
	
	int array_len = json_object_array_length(data_array);
	for (i = 0; i < array_len; i++) {
		struct json_object* item = json_object_array_get_idx(data_array, i);
        // 遍历item中的所有键值对
        json_object_object_foreach(item, key, val) {
            if (json_object_is_type(val, json_type_object)) {
                // 将最内层对象添加到结果中
                json_object_object_add(*result, key, val);
                // 增加引用计数,防止val被释放
                // json_object_get(val);
            }
        }
	}
	
	// 释放原始JSON对象
    //json_object_put(root);
	
    return root;
}

main函数:

cpp 复制代码
#include <stdio.h>
#include <stdlib.h>
#include <json-c/json.h>


/*上面的两个函数*/
int main() {
	char *p = NULL;
	const char* json_str = "{\"type\":\"perf\",\"data\":[{\"apples\":{\"size\":\"512\",\"id\":\"2026\"}},{\"xiaomis\":{\"size\":\"256\",\"id\":\"2025\"}},{\"oppos\":{\"size\":\"128\",\"id\":\"2024\"}}]}";
	struct json_object* result1 = NULL;
	struct json_object* result2 = NULL;
	struct json_object* root = NULL;
	
	result1 = extract_inner_objects(json_str);
	printf("result 1:\n%s\n", json_object_to_json_string(result1));
	//这里只需要put -> result1
	json_object_put(result1);
	
	if((result2 = json_object_new_object())) {
		root = extract_inner_objects2(json_str, &result2);
		if (root != NULL) {
			printf("result 2:\n%s\n", json_object_to_json_string(result2));
			//因为引用计数没有增加,为了完全释放内存,这里需要put -> root
			json_object_put(root);
		} else { 
			//由于解析失败才需要去put ->resul2
			json_object_put(result2);
		}
		
	}
	
	return 0;
}

打印结果:

3,总结

当我们只关心一个json对象中的某些键值对时,而不在关心原始json对象的其他键值对时,我们可以考虑<增加关心对象的引用 json_object_get(xxx)>的方式,使新产生的对象独立于原始对象,这样我们就可以在函数内部中put(root),在使用完result后,再put(result),这样内存就是安全的。其次,当我们还要关心其他键值对时,推荐方法2,即返回原始对象指针和关心的键值对指针。以上内容纯个人理解和总结,欢迎指证。

相关推荐
一直会游泳的小猫3 小时前
homebrew
linux·mac·工具·包管理
寒秋花开曾相惜3 小时前
(学习笔记)4.2 逻辑设计和硬件控制语言HCL(4.2.1 逻辑门&4.2.2 组合电路和HCL布尔表达式)
linux·网络·数据结构·笔记·学习·fpga开发
狂奔的sherry3 小时前
一次由 mount 引发的 Linux 文件系统“错觉”
linux·运维·服务器
小黑要努力3 小时前
智能音箱遇到的问题(一)
linux·运维·git
ch3nyuyu4 小时前
静态库和动态库的制作
linux·运维·开发语言
一口Linux4 小时前
Linux C编程 | 从0实现telnet获取程序终端控制权
linux·运维·c语言
willhuo4 小时前
Certbot工具在CentOS 7.9上申请和配置SSL证书完整教程
linux·centos·ssl
zhangrelay5 小时前
三分钟云课实践速通--大学物理--python 版
linux·开发语言·python·学习·ubuntu·lubuntu
风翼靓崽6 小时前
linux命令杂记 - 杂乱无章
linux·运维·服务器
handler016 小时前
Linux 进程探索:从 PCB 管理到 fork() 的写时拷贝
linux·c语言·c++·笔记·学习