How to transform several JSON objects using json-c?

1.2k Views Asked by At

I wish to use the json-c library to transform all my objects contained in my .json document:

{
    "name": "mathieu",
    "password": "def118e47a2f36b73805b01a5fa3f73b506b98166a929802338db6868e28d942",
    "salt": "nXvtCQEqx8l1uNheIJLoO8VI7049vgIS",
    "email": "[email protected]"
}
{
    "name": "cesar",
    "password": "487b36f3e5a3a74ec3bf2bf48cbc49cde249b08977d22394122c9d512d0e94b4",
    "salt": "Q3QnWFZnLXg8217V1uLaBka6R3CZvCfl",
    "email": "[email protected]"
}

In order to transform them (for the moment I just want to display) I use the following code (I followed the instructions from this Youtube video ) :

#include <stdio.h>
#include <stdlib.h>
#include <json-c/json.h>

//#include "../../include/personnes.h"

//int parseMyJsonPersonne(){
int main(){
    FILE* fichier = NULL;
    char buffer[1024];

    struct json_object *parsed_json;
    struct json_object *name;
    struct json_object *password;
    struct json_object *salt;
    struct json_object *email;

    fichier = fopen("../../data/Personne.json", "r");
    if (fichier != NULL) {
        fread(buffer, 1024, 1, fichier);
    }
    else {
        printf("Une ERREUR est survenue lors du chargement des différents comptes\n");
        return 1;
    }
    fclose(fichier);

    printf("buffer : %s\n", buffer);

    parsed_json = json_tokener_parse(buffer);

    json_object_object_get_ex(parsed_json, "name", &name);
    json_object_object_get_ex(parsed_json, "password", &password);
    json_object_object_get_ex(parsed_json, "salt", &salt);
    json_object_object_get_ex(parsed_json, "email", &email);

    printf("name : %s\n", json_object_get_string(name));
    printf("password : %s\n", json_object_get_string(password));
    printf("salt : %s\n", json_object_get_string(salt));
    printf("email : %s\n", json_object_get_string(email));

    json_object_object_get_ex(parsed_json, "name", &name);
    printf("name 2 : %s\n", json_object_get_string(name));

    free(name);
    free(password);
    free(salt);
    free(email);

    return 0;
}

And this is what the terminal shows me after compilation and execution :

buffer : {
    "name": "mathieu",
    "password": "def118e47a2f36b73805b01a5fa3f73b506b98166a929802338db6868e28d942",
    "salt": "nXvtCQEqx8l1uNheIJLoO8VI7049vgIS",
    "email": "[email protected]"
}
{
    "name": "cesar",
    "password": "487b36f3e5a3a74ec3bf2bf48cbc49cde249b08977d22394122c9d512d0e94b4",
    "salt": "Q3QnWFZnLXg8217V1uLaBka6R3CZvCfl",
    "email": "[email protected]"
}

name : mathieu
password : def118e47a2f36b73805b01a5fa3f73b506b98166a929802338db6868e28d942
salt : nXvtCQEqx8l1uNheIJLoO8VI7049vgIS
email : [email protected]
name 2 : mathieu

So here are my various problems :

  1. How can I go to the next object and know the number of objects in my .json?
  2. I think initializing buffer to 1024 will cause problems if the number of objects is too large, so is there a way to make buffer take the objects one by one?
  3. I have a feeling that the deallocation of memory is not right, did I forget some free ?
1

There are 1 best solutions below

1
On BEST ANSWER

json_tokener_parse() returns the first JSON object it finds in the provided buffer. When you call it you place it in a variable called parsed_json.

Since the second time you try to get the name you keep passing parsed_json,

json_object_object_get_ex(parsed_json, "name", &name);
printf("name 2 : %s\n", json_object_get_string(name));

the same name is retrieved.


What you need to do is continue parsing by calling json_tokener_parse_ex. According to documentation, its interface is

JSON_EXPORT struct json_object* json_tokener_parse_ex( 
    struct json_tokener *   tok,
    const char *    str,
    int     len 
)

where

Parameters

tok a json_tokener previously allocated with json_tokener_new()

str a string with any valid JSON expression, or portion of. This does not need to be null terminated.

len the length of str

It basically will parse the input buffer like json_tokener_parse(), but using internally the token will be able to alert you when something more has to be parsered. This is done by returning json_tokener_continue.

Please refer to documentation in order to get more info. I just quote here the example shown at the documentation link provided above:

json_object *jobj = NULL;
const char *mystring = NULL;
int stringlen = 0;
enum json_tokener_error jerr;

do
{
    mystring = ...  // get JSON string, e.g. read from file, etc...
    stringlen = strlen(mystring);
    jobj = json_tokener_parse_ex(tok, mystring, stringlen);
} while ((jerr = json_tokener_get_error(tok)) == json_tokener_continue);

if (jerr != json_tokener_success)
{
    fprintf(stderr, "Error: %s\n", json_tokener_error_desc(jerr));

    // Handle errors, as appropriate for your application.
}

if (tok->char_offset < stringlen) // XXX shouldn't access internal fields
{
    // Handle extra characters after parsed object as desired.
    // e.g. issue an error, parse another object from that point, etc...
}
// Success, use jobj here.

An alternative solution

As for the other questions, I confirm that first or later you will have problems with your buffer if the number of JSON objects keeps increasing. Dealing with this issue leads to an alternative solution.

Of course you can read them one by one, but you will have to perform some parsering yourself while reading from file. But if you are sure that the file structure will always be the one you posted in your question you are lucky: what you can do is

  1. fopen() the file, like now
  2. Read from file line by line using fgets() instead of fread(). Put data in buffer
  3. Check if the last line contains closing curly bracket }. If yes, go on
  4. Now buffer contains exactly one object. Call json_tokener_parse() like you did before, and print the data parsered with json_object_object_get_ex()
  5. Repeat to point (2) until the end of file is reached
  6. fclose() the file