Search code examples
cjsoncjson

cJSON Key-Value Parsing


I am using cJSON to parse a JSON stored in testdata.json file which looks like this:

{
    "text": "HelloWorld!!",
    "parameters": [{
            "length": 10
        },
        {
            "width": 16
        },

        {
            "height": 16
        }
    ]
}

With the following I can access the text field.

int main(int argc, const char * argv[]) {

    //open file and read into buffer

    cJSON *root = cJSON_Parse(buffer);
    char *text = cJSON_GetObjectItem(root, "text")->valuestring;
    printf("text: %s\n", text); 
}

Note: These parameters are dynamic in the sense that there can be more parameters such as volume, area, etc depending upon what the JSON file contains. The idea is that I have a struct containing all these parameters and I have to check whether the parameters provided in the JSON exists or not and set the values accordingly. The struct looks like:

typedef struct {
   char *path;
   int length;
   int width;
   int height;
   int volume;
   int area;
   int angle;
   int size;
} JsonParameters;

I tried to do it like this:

cJSON *parameters = cJSON_GetObjectItem(root, "parameters");
int parameters_count = cJSON_GetArraySize(parameters);
printf("Parameters:\n");
for (int i = 0; i < parameters_count; i++) {

    cJSON *parameter = cJSON_GetArrayItem(parameters, i);
    int length = cJSON_GetObjectItem(parameter, "length")->valueint;
    int width = cJSON_GetObjectItem(parameter, "width")->valueint;
    int height = cJSON_GetObjectItem(parameter, "height")->valueint;
    printf("%d %d %d\n",length, width, height);
}

This returns Memory access error (memory dumped) plus I have to state what are the keys. As mentioned, I cannot know what will be the parameters.

How can I store the key-value pairs ("length":10, "width":16, "height":16 etc) and how can I check the keys against the valid parameters in JsonParameters?


Solution

  • Here's an example program that walks all the elements of the parameters array from your sample JSON, and prints out the names of the fields of each object in the array:

    #include <stdio.h>
    #include <cJSON.h>
    
    int main(void) {
      const char *json_string = "{\"text\":\"HelloWorld!!\",\"parameters\":[{\"length\":10},{\"width\":16},{\"height\":16}]}";
    
      cJSON *root = cJSON_Parse(json_string);
      cJSON *parameters = cJSON_GetObjectItemCaseSensitive(root, "parameters");
      puts("Parameters:");
      cJSON *parameter;
      cJSON_ArrayForEach(parameter, parameters) {
        /* Each element is an object with unknown field(s) */
        cJSON *elem;
        cJSON_ArrayForEach(elem, parameter) {
          printf("Found key '%s', set to %d\n", elem->string, elem->valueint);     
        }
      }
    
      cJSON_Delete(root);
      return 0;
    }
    

    You can compare each field name against a list of the ones you care about (The easy way is as a bunch of if/else if's and strcmp()), setting the appropriate field of your struct for each one.

    The important thing here is using the cJSON_ArrayForEach macro to walk both the elements of the array (cJSON represents JSON arrays as linked lists, and getting each element by index like in your code makes iterating through the array an O(N^2) operation while this macro is O(N)), and the elements of each object in the array, since you don't know ahead of time which fields are in which object.