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