Search code examples
cjson-c

json-c: Simplest way to access a value from a subarray


{
    "name":"John Doe",
    "dob":"2005-01-03",
    "scores":
    {
        "math":
        {
            "lowest":65,
            "highest":98
        },
        "english":
        {
            "lowest":75,
            "highest":80
        },
        "history":
        {
            "lowest":50,
            "highest":85
        }
    }
}

What is the simplest way to access the highest math score in the above JSON object with json-c?

Is it possible to use something similar to json_object_object_get (jsonobj, "scores.math.highest") ?

If not, do I have to retrieve each individual array to get to the highest score? ie. Get scores, then get math and finally get highest?

Thanks!


Solution

  • Looking at the JSON-C docs it doesn't appear to have an easy way to drill down into the structure. You'll have to do it yourself. Something like this:

    struct json_object *json_object_get_with_keys(
        struct json_object *obj,
        const char *keys[]
    ) {
        while( keys[0] != NULL ) {
            if( !json_object_object_get_ex(obj, keys[0], &obj) ) {
                fprintf(stderr, "Can't find key %s\n", keys[0]);
                return NULL;
            }
    
            keys++;
        }
    
        return obj;
    }
    

    Pass it a null terminated array of keys and it will drill down (or return null).

    const char *keys[] = {"scores", "math", "highest", NULL};
    struct json_object *obj = json_object_get_with_keys(top, keys);
    if( obj != NULL ) {
        printf("%s\n", json_object_to_json_string(obj));
    }
    

    Instead, use JSON-Glib. It has JSONPath which will be more familiar, you can use $.scores.english.highest.

    JsonNode *result_node = json_path_query(
        "$.scores.english.highest",
        json_parser_get_root(parser),
        &error
    );
    if( error != NULL ) {
        fprintf(stderr, "%s", error->message);
        exit(1);
    }
    
    /* It returns a node containing an array. Why doesn't it just return an array? */
    JsonArray *results = json_node_get_array(result_node);
    if( json_array_get_length( results ) == 1 ) {
        printf("highest: %ld\n", json_array_get_int_element(results, 0));
    }
    else {
        fprintf(stderr, "Couldn't find it\n");
    }
    

    It's a little awkward to use, but you can make that easier with some wrapper functions to take care of the scaffolding and error handling.