Board code may not be interested in iterating fully over the JSON blob and instead just wants to lookup a value at a specific JSONPath. Add helpers to facilitate this. Signed-off-by: Ahmad Fatoum <a.fatoum@xxxxxxxxxxxxxx> --- include/jsmn.h | 57 +++++++++++++++++++++++++++++++++ lib/jsmn.c | 86 ++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 143 insertions(+) diff --git a/include/jsmn.h b/include/jsmn.h index 394ffc467487..8f6db8d534f4 100644 --- a/include/jsmn.h +++ b/include/jsmn.h @@ -84,6 +84,63 @@ JSMN_API void jsmn_init(jsmn_parser *parser); JSMN_API int jsmn_parse(jsmn_parser *parser, const char *js, const size_t len, jsmntok_t *tokens, const unsigned int num_tokens); +/** Returns `true` if `token` is a string and equal to `str`. */ +JSMN_API bool jsmn_str_eq(const char *str, const char *json, const jsmntok_t *token); + +/** Returns `true` if `token` is to `str`. */ +JSMN_API bool jsmn_eq(const char *val, const char *json, const jsmntok_t *token); + +/** Returns the token after the value at `tokens[0]`. */ +JSMN_API const jsmntok_t *jsmn_skip_value(const jsmntok_t *tokens); + +/** + * Returns a pointer to the value associated with `key` inside `json` starting + * at `tokens[0]`, which is expected to be an object, or returns `NULL` if `key` + * cannot be found. + */ +JSMN_API const jsmntok_t *jsmn_find_value(const char *key, const char *json, + const jsmntok_t *tokens); + +/** + * Locate the token at `path` inside `json` in a manner similar to JSONPath. + * + * Example: + * \code + * { + * "date": "...", + * "product": { + * "serial": "1234", + * } + * } + * \endcode + * + * To locate the serial number in the JSON object above, you would use the + * JSONPath expression "$.product.serial". The same thing can be accomplished + * with this call: + * + * \code + * const char *path[] = {"product", "serial", NULL}; + * const jsmntok_t *token = jsmn_locate(path, json, tokens); + * \endcode + * + * \remark This function cannot search inside arrays. + * + * @param path Null-terminated list of path elements. + * @param json JSON string to search in. + * @param tokens Tokens for `json`. + * + * @return Pointer to the value token or `NULL` if the token could not be found. + */ +JSMN_API const jsmntok_t *jsmn_locate(const char *path[], const char *json, + const jsmntok_t *tokens); + +/** + * Similar to `jsmn_locate` but returns a copy of the value or `NULL` if the + * value does not exist or is not a string. The caller takes ownership of the + * pointer returned. + */ +JSMN_API char *jsmn_strcpy(const char *path[], const char *json, const jsmntok_t *tokens); + #ifdef __cplusplus } #endif diff --git a/lib/jsmn.c b/lib/jsmn.c index 3a68f89337fc..7bdcc90f2f4b 100644 --- a/lib/jsmn.c +++ b/lib/jsmn.c @@ -369,3 +369,89 @@ JSMN_API void jsmn_init(jsmn_parser *parser) { parser->toknext = 0; parser->toksuper = -1; } + +JSMN_API bool jsmn_eq(const char *val, const char *json, const jsmntok_t *token) +{ + size_t token_size = token->end - token->start; + return strlen(val) == token_size + && strncmp(json + token->start, val, token_size) == 0; +} + +JSMN_API bool jsmn_str_eq(const char *str, const char *json, const jsmntok_t *token) +{ + return token->type == JSMN_STRING && jsmn_eq(str, json, token); +} + +JSMN_API const jsmntok_t *jsmn_skip_value(const jsmntok_t *tokens) +{ + int max_index = tokens[0].end; + do { + ++tokens; + } while (tokens[0].start < max_index); + return &tokens[0]; +} + +JSMN_API const jsmntok_t *jsmn_find_value(const char *key, const char *json, + const jsmntok_t *tokens) +{ + int items; + if (tokens[0].type != JSMN_OBJECT || tokens[0].size == 0) + return NULL; + + items = tokens[0].size; + ++tokens; + + do { + if (jsmn_str_eq(key, json, tokens)) + return &tokens[1]; + tokens = --items ? jsmn_skip_value(&tokens[1]) : NULL; + } while (tokens); + + return NULL; +} + +JSMN_API const jsmntok_t *jsmn_locate(const char *path[], const char *json, + const jsmntok_t *tokens) +{ + int i = 0; + while (path[i] != NULL) { + const jsmntok_t *value = jsmn_find_value(path[i], json, tokens); + if (!value) + return NULL; + + switch (value->type) { + case JSMN_OBJECT: + case JSMN_ARRAY: + tokens = value; + ++i; + break; + case JSMN_UNDEFINED: + case JSMN_STRING: + case JSMN_PRIMITIVE: + return value; + } + } + + return tokens; +} + +JSMN_API char *jsmn_strcpy(const char *path[], const char *json, + const jsmntok_t *tokens) +{ + const jsmntok_t *node; + int value_size; + char *value; + + node = jsmn_locate(path, json, tokens); + if (!node || node->type != JSMN_STRING) + return NULL; + + value_size = node->end - node->start; + value = malloc(value_size + 1); + if (value) { + strncpy(value, json + node->start, value_size); + value[value_size] = '\0'; + } + + return value; +} -- 2.30.2