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

New JSMN_PARENT_CNT to allow enhanced enumeration of JSON Structures. #232

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
97 changes: 97 additions & 0 deletions example/jsmn_tools.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
#define JSMN_STRICT
#define JSMN_PARENT_LINKS
#define JSMN_PARENT_CNT
#include "../jsmn.h"
#include "jsmn_tools.h"
#include <string.h>
#include <stdio.h>


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

jsmntok_t *
_next(jsmntok_t *t)
{
return t + 1 + t->size ;
}

jsmntok_t *
jsmn_get_next(jsmntok_t *first, jsmntok_t *t)
{
if (t == 0) {
return first ;
}
if (t + t->size < first + first->size) {
return _next(t) ;
}
return 0 ;
}

int
jsmn_object_enum (const char *js, jsmntok_t *t, size_t count,
jsmn_key_val_t * out, size_t out_count)
{
jsmntok_t *start = t;
jsmntok_t *key;
jsmntok_t *val;

if (t->type != JSMN_OBJECT) {
printf("OBJECT EXPECTED!\r\n");
return -1;
}

t = t + 1 ;

while (t < start + count) {
if (t->type == JSMN_STRING) {
key = t ;
val = t + 1 ;
t += 1 ;

} else if (t->type == JSMN_OBJECT) {
printf("UNEXPECTED: OBJECT\r\n");
return -1; ;
} else if (t->type == JSMN_PRIMITIVE) {
printf("UNEXPECTED: '%.*s'\r\n", t->end - t->start, js + t->start);
return -1;
} else {
printf("UNEXPECTED:\r\n");
return -1;
}

#ifdef DEBUG
printf("KEY: '%.*s': ", key->end - key->start, js + key->start);

if ((val->type == JSMN_STRING) || (val->type == JSMN_PRIMITIVE)) {
printf("VAL: '%.*s'\r\n", val->end - val->start, js + val->start);
} else {
printf("VAL: '%s\r\n", (val->type == JSMN_OBJECT) ? "OBJECT" : "ARRAY");
}
#endif

int i ;
for (i=0; i<out_count; i++) {
if (!out[i].value && (jsmn_eq(js, key, out[i].key) == 0)) {
out[i].value = val ;
}
}

t = _next (t) ;

}

return 0 ;
}

int jsmn_copy_str (const char *js, jsmntok_t *t, char * sz, size_t size)
{
return snprintf (sz, size, "%.*s", t->end - t->start, js + t->start) ;
}
121 changes: 121 additions & 0 deletions example/jsmn_tools.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
/**
* @file jsmn_tools.h
*
* @brief An Auxiliary Header for JSMN Library Enabling Enhanced JSON
* Structure Enumeration
*
* This header file, designed to complement the jsmn.h library, is tailored
* for use in conjunction with a specially defined JSMN_PARENT_CNT during the
* compilation of jsmn.h. This subtle customisation allows for refined and
* advanced JSON structure traversal, making it possible to iteratively explore
* nested sub-objects within the JSON hierarchy.
*
*/

#ifndef __JSNM_TOOLS_H__
#define __JSNM_TOOLS_H__

#define JSMN_HEADER
#define JSMN_PARENT_LINKS
#define JSMN_PARENT_CNT
#include "../jsmn.h"


#include <stddef.h>

#ifdef __cplusplus
extern "C" {
#endif


/**
* Structure for representing a key-value pair in a JSON object.
*
* This structure is used to associate a JSON object key (a string) with its
* corresponding value (a jsmntok_t token). It is employed to store and manage
* key-value pairs extracted from a JSON object during parsing.
*
* @param key A pointer to the key string.
* @param value A pointer to the jsmntok_t token of the value.
*/
typedef struct jsmn_key_val_s {
const char * key ;
jsmntok_t * value ;
} jsmn_key_val_t ;

/**
* Enumerate and extract key-value pairs from a JSON object.
*
* This function takes a JSON object represented by a jsmntok_t pointer
* and extracts its key-value pairs. It then updates an array of jsmn_key_val_t
* structures, associating the keys with their corresponding values.
*
* This function will not traverse nested structures or arrays.
*
*
* @param js A pointer to the JSON string.
* @param t A pointer to the JSON object token.
* @param count The number of tokens in the JSON object.
* @param out An array of jsmn_key_val_t structures to store the key-value pairs.
* @param out_count The size of the 'out' array.
*
* @return 0 if successful, -1 if an error occurs (e.g., unexpected JSON structure).
*/
int jsmn_object_enum (const char *js, jsmntok_t *t, size_t count,
jsmn_key_val_t * out, size_t out_count) ;

/**
* Get the next JSON token in a sequence.
*
* This function takes the current token pointer 't' and returns a pointer
* to the next token in a JSON token sequence. If 't' is NULL, it returns
* the first token in the sequence.
*
* This function will not traverse nested structures or arrays.
*
* @param first A pointer to the first token in the JSON token sequence.
* @param t A pointer to the current token.
*
* @return A pointer to the next token in the sequence or NULL if there are no more tokens.
*/
jsmntok_t * jsmn_get_next(jsmntok_t *first, jsmntok_t *t);

/**
* Compare a JSON token with a given string.
*
* This function compares the content of a JSON token with a provided string.
* It checks if the token represents a string and if the string's length matches
* the length of the provided string. Additionally, it verifies if the actual content
* of the token matches the provided string.
*
* @param json A pointer to the JSON string.
* @param t A pointer to the JSON token to be compared.
* @param s A pointer to the string for comparison.
*
* @return 0 if the token content matches the provided string, -1 otherwise.
*/
int jsmn_eq(const char *json, jsmntok_t *t, const char *s);

/**
* Copy the content of a JSON string token to a character buffer.
*
* This function copies the content of a JSON string token to a character buffer.
* It uses snprintf to ensure proper handling of the destination buffer size
* and takes care of extracting the substring based on the token's start and end indices.
*
* @param js A pointer to the JSON string.
* @param t A pointer to the JSON object token.
* @param sz A pointer to the destination buffer.
* @param size The size of the destination buffer.
*
* @return The number of characters written to the destination buffer (excluding the null terminator).
* If the return value is greater than or equal to 'size', it indicates truncation.
*/
int jsmn_copy_str (const char *js, jsmntok_t *t, char * sz, size_t size);


#ifdef __cplusplus
}
#endif

#endif /* __JSNM_TOOLS_H__ */
178 changes: 178 additions & 0 deletions example/parentcnt.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
#include "jsmn_tools.h"
#include <stdint.h>
#include <stdlib.h>
#include <errno.h>
#include <stdio.h>
#include <stdbool.h>
#include <string.h>


static int read_file(const char * filename, char ** pbuffer);

/**
* Main function showcasing JSON parsing using JSMN with defined JSMN_PARENT_CNT.
*
* This function reads a sample JSON file, utilizes the JSMN library with a
* specifically defined JSMN_PARENT_CNT during compilation, and extracts information
* about operations to execute. The JSON structure includes data such as serial numbers,
* indices, and states for outlets. The provided example JSON is as follows:
*
* {
* "version": "0.1",
* "endpoint": "outlets",
* "cmd": "set",
* "switches": [
* {
* "serial": "12345",
* "index": [0, 1, 2, 3],
* "state": ["open", "close", "open", "close"]
* },
* {
* "serial": "67890",
* "index": [0, 1, 2, 3],
* "state": ["open", "open", "open", "close"]
* }
* ],
* "test": {
* "test2": {
* "key": "value"
* }
* }
* }
*
* The expected output would be:
*
* serial 12345: index 0 to open
* serial 12345: index 1 to close
* serial 12345: index 2 to open
* serial 12345: index 3 to close
* serial 67890: index 0 to open
* serial 67890: index 1 to open
* serial 67890: index 2 to open
* serial 67890: index 3 to close
*/
int main()
{
jsmn_parser p;
jsmntok_t * t ;
jsmn_key_val_t out[] = { {"endpoint", 0}, {"cmd", 0} , {"switches", 0} };
char * buffer ;
int len = read_file ("parentcnt.json", &buffer) ;

if (len <= 0) {
return 1;
}

printf("parsing json...\r\n");
jsmn_init(&p);
int r = jsmn_parse(&p, buffer, len, 0, 0); // content is the char array holding the json content

printf("%d objects found!\r\n", r);
if (r <= 0) {
printf("Error invalid json\r\n");
return 1;
}

t = malloc (r * sizeof(jsmntok_t)) ;
if (!t) {
printf("Error out of memory\r\n");
return 1;
}

jsmn_init(&p);
r = jsmn_parse (&p, buffer, len, t, r) ;
jsmn_object_enum (buffer, t, r, out, 3) ;
printf("\r\n\r\n");

jsmntok_t * switches = out[2].value;
if (switches) {
/* A "switches" array is present.
* Assign "next" to the first object in the array.
*/
jsmntok_t * next = switches + 1 ;

/* Enumerate over all the objects in the array. */
do {
/* out3 is declared here to clear the `values` of out3 to NULL.
* jsmn_object_enum only assigns a value if it is NULL.
*/
jsmn_key_val_t out3[] = { {"serial", NULL}, {"index", NULL} ,
{"state", NULL} } ;
jsmn_object_enum (buffer, next, next->size, out3, 3) ;
jsmntok_t * serial = out3[0].value ;

if (serial) {
/* The serial key is present so it is a valid object. */
char szserial[12] ;
/* Extract the two arrays we will use to switch the outlets. */
jsmntok_t * next_index = out3[1].value + 1 ;
jsmntok_t * next_state = out3[2].value + 1 ;

jsmn_copy_str (buffer, serial, szserial, 12) ;

while (next_index && next_state) {
/* Enumerate over all the indices in the arrays and switch the outlets. */
char szindex[8] ;
char szstate[8] ;

jsmn_copy_str (buffer, next_index, szindex, 8) ;
jsmn_copy_str (buffer, next_state, szstate, 8) ;

/* We don't really switch, just print the result. */
printf("serial %s: index %s to %s\r\n",
szserial, szindex, szstate) ;

next_index = jsmn_get_next (out3[1].value, next_index) ;
next_state = jsmn_get_next (out3[2].value, next_state) ;

}

}

} while ((next = jsmn_get_next (switches, next))) ;

}

/* Don't forget to free the allocated memory when you're done */
free(buffer);

return 0;
}


int read_file(const char * filename, char ** pbuffer)
{
FILE *file;
char *buffer;
long len;

/* Open the file in binary read mode */
file = fopen(filename, "rb");

if (file == NULL) {
printf("Error opening the file");
return 0;
}

/* Get the file length */
fseek(file, 0, SEEK_END);
len = ftell(file);
rewind(file);

/* Allocate memory for the buffer */
buffer = (char *)malloc(len);

if (buffer == NULL) {
printf("Error allocating memory");
return 0;
}

/* Read the entire file into the buffer */
len = fread(buffer, 1, len, file);
/* Close the file */
fclose(file);

*pbuffer = buffer;

return len;
}
Loading