从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 小时前
从零入门计算机网络系列(十一):HTTP协议进阶——从重定向原理到GET/POST与Fiddler抓包
linux·服务器·开发语言·网络·c++·tcp/ip·html
AI_Lab015 小时前
宝塔用了两年,有没有必要换成轻量方案?
linux·运维·服务器
by209995 小时前
Linux GDB 调试与 CGDB 使用
linux·运维·服务器·经验分享
星恒随风5 小时前
Linux网络编程:应用层协议究竟是什么?从网络计算器到TCP Socket抽象基类完整实现
linux·网络·笔记·tcp/ip
程序员大阳7 小时前
副队长JavaScript教程(65)-详解JSON:轻量级数据交换格式
javascript·json·序列化·反序列化·stringify·parse
feng_you_ying_li8 小时前
linux之网络层
linux·运维·网络
未来之窗软件服务8 小时前
Linux 运维日志删除的多种方式—东方仙盟
linux·运维·服务器·仙盟创梦ide·东方仙盟
小雉系统8 小时前
BcoreOS实战:用一个升级包,让Rocky Linux跑进Debian服务器
linux·运维·服务器·debian
thethefighter8 小时前
快速预览Word、WPS秒开,支持空格弹窗
linux·word·wps·快速预览·空格弹窗·nhdeepview
用户8351414049499 小时前
把日志和寄存器值喂给AI做故障定位,我的实际问法
linux·ubuntu