How to use json_incref, json_decref, and json_get to look at a piece of a json file?

195 Views Asked by At

I have been struggling to understand the proper use of json_decref and json_incref in combination with the json_get functions to isolate and manipulate a small piece of a larger json file without keeping the whole thing in memory. This question is in the context of the jansson.h library and the C programming language.

My question is, have I done the right thing here? In this function blockload below, am I using json_incref and json_decref in the orthodox way? I'm asking because I'm having trouble parsing what was said in the jansson documentation about json_incref and also in another posting here.

I have been struggling to understand the proper use of json_decref and json_incref in combination with the json_get functions to isolate and manipulate a small piece of a larger json file without keeping the whole thing in memory. This question is in the context of the jansson.h library and the C programming language.

My question is, have I done the right thing here? In this function blockload below, am I using json_incref and json_decref in the orthodox way? I'm asking because I'm having trouble parsing what was said in the jansson documentation about json_incref and also in another posting here.

The following code snippet reads in a file called "file.json" that has the data

{"myarray" : [[["aaa","aab","aac"],["aba","abb","abc"],["aca","acb","acc"] ],[["baa","bab","bac"],["bba","bbb","bbc"],["bca","bcb","bcc"] ],[["caa","cab","cac"],["cba","cbb","cbc"],["cca","ccb","ccc"] ]]}

The code snippet is as follows

#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <jansson.h>

#define INPUTFILE "./file.json"

json_t *blockload(char *inputfile, char *blockname);

int main(void){
    
    json_error_t error;
    json_t *test;
    
    int length;
    
    const char *mystring_const;
    
    json_t *myarray;
    
    json_t *myelement;
    
    myarray = blockload(INPUTFILE, "myarray");
    
    myelement =json_array_get(json_array_get(json_array_get(myarray, 0), 0), 0);
    
    mystring_const = json_string_value(myelement);
    
    printf("%s \n", mystring_const);
    
    json_decref(myarray);
    
    return(0);
    
}
 
json_t *blockload(char *inputfile, char *blockname){
    
    json_error_t error;
    json_t *test, *myarray;
    
    test = json_load_file(INPUTFILE, 0, &error);
    
    myarray = json_object_get(test, blockname);
    
    json_incref(myarray);
    
    json_decref(test);
    
    return(myarray);
}

In this case, I am trying to look at the array associated with the object "myarray", but one could imagine the json file has many more pieces, and I don't want to have to store them all in memory while I work with the array. I also don't want to clutter up the main routine with the details of how I load in this array of interest, so I have written this function blockload to hide the clutter.

While this code compiles and gives the answer I expect and produces no problems from valgrind, I'm worried it may still contain some memory use issues. So I'm asking the experts...

1

There are 1 best solutions below

0
On

json_load_file() is an object creation function. It returns an owned (by the caller) reference to a new JSON entity of unspecified type. The reference count of that entity will be 1.

json_object_get() returns a borrowed reference to the designated member of the specified JSON object. Since you plan to hold on to that reference, you should increase its reference count, which you in fact do. It will not be collected while its reference count exceeds 0. You can think of increasing the reference count of a borrowed reference as converting it into an owned reference.

You indeed do the right thing by decreasing the reference count of the larger object before losing the reference to it. It's not your responsibility to know the actual reference count at any particular time, but in fact you do know in this case that that will decrease the reference count of this object to 0, making it eligible for clean up.

On the other hand, the reference count of the array you pulled out is still above zero (because you increased it and at that point have not decreased it again). You can therefore rely on that object not to be cleaned up. This is apparently what you set out to achieve.

While this code compiles and gives the answer I expect and produces no problems from valgrind, I'm worried it may still contain some memory use issues.

The API documentation does not guarantee that JSON entities actually will be cleaned up as soon as their reference counts drop to zero, though I expect that will normally be the case. But even if that were deferred in this case, Jansson does not provide a better alternative.

It is clean and valid to decrease the array's reference count before the program terminates. After doing so, you should not attempt to use that reference further, and indeed you don't.

Your use of the array element and its string value are also within the norms for Jansson too, so overall, everything looks by-the-book to me.