Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Closed
sudharkrish opened this issue Oct 3, 2018 · 6 comments

Comments

@sudharkrish
Copy link

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
Copy link
Collaborator

pt300 commented Oct 3, 2018

  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 heap buffer overflow on some inputs #125

@sudharkrish
Copy link
Author

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:

  • User: johndoe
  • Admin: false
  • UID: 1000
  • Groups:
    • users
    • wheel
    • audio
    • video
  • Groups:
    • ip_addr
    • 127.0.0.1
      Unexpected key: port_num
      Unexpected key: 4444

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 <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "../jsmn.h"

/*

  • A small example of jsmn parsing when JSON structure is known and number of
  • tokens is predictable.
    */

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
Copy link
Author

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
Copy link
Collaborator

pt300 commented Oct 4, 2018

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
Copy link
Author

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 <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "../jsmn.h"

/*

  • A small example of jsmn parsing when JSON structure is known and number of
  • tokens is predictable.
    */

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:

  • User: johndoe
  • Admin: false
  • UID: 1000
  • Groups:
    ARRAY group size=>t[i+1].size = 0
    • users
    • wheel
    • audio
    • video
  • Groups:
    key-value group size=>t[i+1].size = 0
    • 127.0.0.1
    • 4444

@pt300
Copy link
Collaborator

pt300 commented Oct 4, 2018

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

@pt300 pt300 closed this as completed Apr 24, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants