zserge / jsmn

Jsmn is a world fastest JSON parser/tokenizer. This is the official repo replacing the old one at Bitbucket
MIT License
3.67k stars 781 forks source link

Does jsmn support parsing group with key, value pair. Example for parsing group with key, value pair #141

Closed sudharkrish closed 5 years ago

sudharkrish commented 6 years ago

Can you let me know, if a group with key, value pair is supported.

For example, if I have a json file, that is read by my program, and lets say the contents of the json file is below: { "plat_conf": { "ip_addr": "127.0.0.1", "port_num": "4444" }
}

The program simple.c, under example directory, has an example of a group that has an array of strings.

I tried to modify simple.c, to parse a group with key, value pair, and ran into issues.

Can you please let me know below:

  1. Does jsmn support parsing group with key, value pair.
  2. If yes, can you please update simple.c, by adding a group with key, value pair, in JSON_STRING in that program.
pt300 commented 6 years ago
  1. Yes, JSMN is alright with object as a value inside and object. That's how JSON works.
  2. You do it almost the same way as with an array with the difference that you have keys and values right after them in the tokens list. So, you take first key, if it's size is 1 then the next token is a value, otherwise if it's 0 then next token will be another key. It's good to remember that to avoid errors like pointed out in #125
sudharkrish commented 6 years ago

Thanks for checking. I tried few options, but ran into issues.

I made changes to simple.c example program in your repo.

Here below is the JSON_STRING updatedwith a group with key, value pair:

static const char *JSON_STRING = "{\"user\": \"johndoe\", \"admin\": false, \"uid\": 1000,\n " "\"groups\": [\"users\", \"wheel\", \"audio\", \"video\"],\n " "\"plat_conf\": {\"ip_addr\": \"127.0.0.1\",\"port_num\": \"4444\"}}"; And this the section I added to parse the group with key, value pairs:

else if (jsoneq(JSON_STRING, &t[i], "plat_conf") == 0) { int j; printf("- Groups:\n"); if (t[i+1].type != JSMN_OBJECT) { continue; / We expect groups to be an array of strings / } for (j = 0; j < t[i+1].size; j++) { jsmntok_t g = &t[i+j+2]; printf(" %.*s\n", g->end - g->start, JSON_STRING + g->start); } i += t[i+1].size + 1; }

Here is my output:

As you can see above the parsing fails. Can you give some code snippets on what changes are needed to parse the key, value pair, in the example above.

Here is the complete simple.c, with my changes:

include

include

include

include "../jsmn.h"

/*

static const char *JSON_STRING = "{\"user\": \"johndoe\", \"admin\": false, \"uid\": 1000,\n " "\"groups\": [\"users\", \"wheel\", \"audio\", \"video\"],\n " "\"plat_conf\": {\"ip_addr\": \"127.0.0.1\",\"port_num\": \"4444\"}}";

static int jsoneq(const char json, jsmntok_t tok, const char *s) { if (tok->type == JSMN_STRING && (int) strlen(s) == tok->end - tok->start && strncmp(json + tok->start, s, tok->end - tok->start) == 0) { return 0; } return -1; }

int main() { int i; int r; jsmn_parser p; jsmntok_t t[128]; / We expect no more than 128 tokens /

jsmn_init(&p);
r = jsmn_parse(&p, JSON_STRING, strlen(JSON_STRING), t, sizeof(t)/sizeof(t[0]));
if (r < 0) {
    printf("Failed to parse JSON: %d\n", r);
    return 1;
}

/* Assume the top-level element is an object */
if (r < 1 || t[0].type != JSMN_OBJECT) {
    printf("Object expected\n");
    return 1;
}

/* Loop over all keys of the root object */
for (i = 1; i < r; i++) {
    if (jsoneq(JSON_STRING, &t[i], "user") == 0) {
        /* We may use strndup() to fetch string value */
        printf("- User: %.*s\n", t[i+1].end-t[i+1].start,
                JSON_STRING + t[i+1].start);
        i++;
    } else if (jsoneq(JSON_STRING, &t[i], "admin") == 0) {
        /* We may additionally check if the value is either "true" or "false" */
        printf("- Admin: %.*s\n", t[i+1].end-t[i+1].start,
                JSON_STRING + t[i+1].start);
        i++;
    } else if (jsoneq(JSON_STRING, &t[i], "uid") == 0) {
        /* We may want to do strtol() here to get numeric value */
        printf("- UID: %.*s\n", t[i+1].end-t[i+1].start,
                JSON_STRING + t[i+1].start);
        i++;
    } else if (jsoneq(JSON_STRING, &t[i], "groups") == 0) {
        int j;
        printf("- Groups:\n");
        if (t[i+1].type != JSMN_ARRAY) {
            continue; /* We expect groups to be an array of strings */
        }
        for (j = 0; j < t[i+1].size; j++) {
            jsmntok_t *g = &t[i+j+2];
            printf("  * %.*s\n", g->end - g->start, JSON_STRING + g->start);
        }
        i += t[i+1].size + 1;
    } else if (jsoneq(JSON_STRING, &t[i], "plat_conf") == 0) {
        int j;
        printf("- Groups:\n");
        if (t[i+1].type != JSMN_OBJECT) {
            continue; /* We expect groups to be an array of strings */
        }
        for (j = 0; j < t[i+1].size; j++) {
            jsmntok_t *g = &t[i+j+2];
            printf("  * %.*s\n", g->end - g->start, JSON_STRING + g->start);
        }
        i += t[i+1].size + 1;
    } else {
        printf("Unexpected key: %.*s\n", t[i].end-t[i].start,
                JSON_STRING + t[i].start);
    }
}
return EXIT_SUCCESS;

}

sudharkrish commented 6 years ago

It will be great, if you can provide some code snippets, on what changes are needed to parse group with key, value pair in the modified simple.c that I had pasted previously.

pt300 commented 6 years ago

You're on the right track. In the for loop you added you now have to, for each token, see if it's size attribute is equal 1. Then the next token is a value.

for (j = 0; j < t[i+1].size; j++) {
    jsmntok_t *g = &t[i+j+2];
    printf("  * %.*s\n", g->end - g->start, JSON_STRING + g->start);
    if(g->size == 1) {  //key has a value
        j++;    //jump to next token
        g = &t[i+j+2];  //you know the drill
        printf("  * %.*s\n", g->end - g->start, JSON_STRING + g->start);
    }
}
sudharkrish commented 6 years ago

Thanks for your inputs.

I tried your suggestion, but ran into issue. For example, since I have 2 rows for key-value pair, under the group plat_info, I was expecting, t[1+1].size will be 2. But I am getting 0 for t[1+1].size, so its NOT looping all the entries. And same thing even with the group array, which is existing code in the repo.

Ideally, I would like to compare the key field, and then print the value. For example, jsoneq(... "ip_addr") then print the value field.

Can you provide some example code that can do above.

Here is my modified simple.c, along with the output:

include

include

include

include "../jsmn.h"

/*

static const char *JSON_STRING = "{\"user\": \"johndoe\", \"admin\": false, \"uid\": 1000,\n " "\"groups\": [\"users\", \"wheel\", \"audio\", \"video\"],\n " "\"plat_conf\": {\"ip_addr\": \"127.0.0.1\",\"port_num\": \"4444\"}}";

static int jsoneq(const char json, jsmntok_t tok, const char *s) { if (tok->type == JSMN_STRING && (int) strlen(s) == tok->end - tok->start && strncmp(json + tok->start, s, tok->end - tok->start) == 0) { return 0; } return -1; }

int main() { int i; int r; jsmn_parser p; jsmntok_t t[128]; / We expect no more than 128 tokens /

jsmn_init(&p);
r = jsmn_parse(&p, JSON_STRING, strlen(JSON_STRING), t, sizeof(t)/sizeof(t[0]));
if (r < 0) {
    printf("Failed to parse JSON: %d\n", r);
    return 1;
}

/* Assume the top-level element is an object */
if (r < 1 || t[0].type != JSMN_OBJECT) {
    printf("Object expected\n");
    return 1;
}

/* Loop over all keys of the root object */
for (i = 1; i < r; i++) {
    if (jsoneq(JSON_STRING, &t[i], "user") == 0) {
        /* We may use strndup() to fetch string value */
        printf("- User: %.*s\n", t[i+1].end-t[i+1].start,
                JSON_STRING + t[i+1].start);
        i++;
    } else if (jsoneq(JSON_STRING, &t[i], "admin") == 0) {
        /* We may additionally check if the value is either "true" or "false" */
        printf("- Admin: %.*s\n", t[i+1].end-t[i+1].start,
                JSON_STRING + t[i+1].start);
        i++;
    } else if (jsoneq(JSON_STRING, &t[i], "uid") == 0) {
        /* We may want to do strtol() here to get numeric value */
        printf("- UID: %.*s\n", t[i+1].end-t[i+1].start,
                JSON_STRING + t[i+1].start);
        i++;
    } else if (jsoneq(JSON_STRING, &t[i], "groups") == 0) {
        int j;
        printf("- Groups:\n");
        if (t[i+1].type != JSMN_ARRAY) {
            continue; /* We expect groups to be an array of strings */
        }
        printf("ARRAY group size=>t[i+1].size = %d\n", t[1+1].size);
        for (j = 0; j < t[i+1].size; j++) {
            jsmntok_t *g = &t[i+j+2];
            printf("  * %.*s\n", g->end - g->start, JSON_STRING + g->start);
        }
        i += t[i+1].size + 1;
    } else if (jsoneq(JSON_STRING, &t[i], "plat_conf") == 0) {
        int j;
        printf("- Groups:\n");

        if (t[i+1].type != JSMN_OBJECT) {
            continue; // We expect groups to be an array of strings
        }

        printf("KEY-VALUE group size=>t[i+1].size = %d\n", t[1+1].size);

        for (j = 0; j < t[i+1].size; j++) {

            jsmntok_t *g = &t[i+j+2];

            //printf("  * %.*s\n", g->end - g->start, JSON_STRING + g->start);

            if(g->size == 1) {  //key has a value
                j++;    //jump to next token
                g = &t[i+j+2];  //you know the drill
                printf("  * %.*s\n", g->end - g->start, JSON_STRING + g->start);
            }
        }

        //i += t[i+1].size + 1;
        i++;
        j = 1;
        //for (j = 0; j < t[i+1].size; j++) {

            jsmntok_t *g = &t[i+j+2];

            //printf("  * %.*s\n", g->end - g->start, JSON_STRING + g->start);

            if(g->size == 1) {  //key has a value
                j++;    //jump to next token
                g = &t[i+j+2];  //you know the drill
                printf("  * %.*s\n", g->end - g->start, JSON_STRING + g->start);
            }
        //}

        i += t[i+1].size + 1;
        i += j + 1;

    }
    else {
        printf("Unexpected key: %.*s\n", t[i].end-t[i].start,
                JSON_STRING + t[i].start);
    }
}
return EXIT_SUCCESS;

}

OUTPUT:

pt300 commented 6 years ago

it's supposed to be i+1 not 1+1 when you print the size.