1
{
    "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!

Phil
  • 2,008
  • 1
  • 17
  • 23
  • I think you need to learn some basic Javascript and about associative arrays it the language. – Some programmer dude Feb 18 '17 at 01:30
  • I know how to use Javascript and JSON. I'm asking how to access the value (in the simplest possible way) using json-c, which is a JSON implementation in C. – Phil Feb 18 '17 at 01:32
  • Then you *do* know about associative arrays in Javascript? So in a Javascript program how would you access these elements of the lists? What if you try something similar in the C API? – Some programmer dude Feb 18 '17 at 01:34
  • In Javascript, I'd use object['scores']['math']['highest']. I've tried similar variations in json-c but can't get the desired result. – Phil Feb 18 '17 at 01:38
  • Your edit changes the question considerably. But it doesn't (fortunately) change the problem on how to access the sub-objects. Using your API, do you know how to access e.g. `object['name']`? Then you actually know how to access the object `object['scores']` and all it sub-objects. – Some programmer dude Feb 18 '17 at 02:22
  • I know how to access object['scores'] via json_object_object_get (jsonobj, "scores"). "scores" is a char *. I've tried every possible variation that I can think of in place of scores (eg. scores.math, scores->math, scores[math], scores['math'], etc) but nothing works. The function only seems to retrieve a single top-level value, hence the question if it's possible. Worst case is a recursive function, but I'd like to avoid it. – Phil Feb 18 '17 at 02:30
  • I'm sorry, but I thought it would be as simple as doing something like `scores_object = json_object_object_get (jsonobj, "scores"); math_object = json_object_object_get (scores_object, "math");` and from that get the `"highest"` value from the `math_object`. – Some programmer dude Feb 18 '17 at 09:30
  • It actually is that simple, however I was hoping json-c had a nicer way of doing things, especially considering sometimes the JSON that I am traversing has a large number of sub-objects. A function that handles everything seems unavoidable though. – Phil Feb 18 '17 at 20:10

1 Answers1

2

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.

Schwern
  • 153,029
  • 25
  • 195
  • 336
  • Sorry about that. I am using a valid JSON file. I had typed out the example given above but used square brackets instead of curly braces. I've fixed the example. – Phil Feb 18 '17 at 01:56
  • @Phil Ok. I updated it with an answer, and a recommendation to try JSON-Glib instead. – Schwern Feb 18 '17 at 02:41
  • I'm glad it wasn't just me that couldn't find it in the docs. I like JSON-Glib, but I've decided to stick to json-c for now. I already have a function that is similar to yours, but thank you for providing a code sample. I've tested it and it works well. – Phil Feb 18 '17 at 04:30