从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,即返回原始对象指针和关心的键值对指针。以上内容纯个人理解和总结,欢迎指证。

相关推荐
坚持就完事了2 小时前
Linux的which命令
linux·运维·服务器
和小潘一起学AI2 小时前
centOS安装neo4j
linux·运维·服务器
HealthScience3 小时前
H20服务器多卡运行有错误gpu_partition ,tmux错误
linux·运维·服务器
_Emma_3 小时前
【Raspberry PI】Raspberry Pi HEVC (H.265) 硬件解码器
linux·驱动开发·视频编解码
RisunJan3 小时前
Linux命令-netstat(查看Linux中网络系统状态信息)
linux·运维·服务器
Hello.Reader3 小时前
双卡 A100 + Ollama 生产部署从安装、踩坑、调优到最终可上线方案
linux·人工智能·算法
SPC的存折3 小时前
1、MySQL数据库基础
linux·运维·数据库·mysql
无忧.芙桃3 小时前
进程之环境变量
linux·运维·服务器
Wanliang Li3 小时前
Linux驱动——input子系统
linux·驱动开发·input