Search code examples
cparsingpipecjson

Avoiding core dump in cJSON parse endless loop while replacing nested values


I have a cJSON tree in which I am saving the x and y values of a cosine I am generating in a while loop.

It looks like this:

"myXY": [{
        "x":    2.0110001564025879,
        "y":    0.99761265516281128
    }]

myXY is an Array, that I am adding to the cJSON: "myJSON", like this:

cJSON *myXY = NULL;
myXY = cJSON_AddArrayToObject(myJSON, "myXY");

I then add the values by creating an object "myNestedArray" and adding it to "myXY":

cJSON_AddNumberToObject(myNestedArray, "x", pOutput->x);
cJSON_AddNumberToObject(myNestedArray, "y", pOutput->y);


cJSON_AddItemToArray(myXY, myNestedArray);

(pOutput being a struct I am passing to my "parse_output_to_json"-function.)

Finally I am piping my "cJSON-string" with stdout to my second program "receiver.c".

char *string = cJSON_Print(myJSON);
write(1, string, strlen(string));

Up until now everything works fine.

Now I want to rewrite the "y" in "myXY" inside the cJSON-tree with sinf("x" /*the "x" value in "myXY"*/)

As it turns out, this is way more complicated then I thought:

int main() {

  /* variables*/
  char buffer[50];
  char *string = NULL;

  /* master-loop*/
  while (1) {
    /* cJSON variables*/
    cJSON *myNewJSON = NULL;
    cJSON *myNewXY = NULL;
    cJSON *xValJSON = NULL;
    
    /* parsing the pipe*/
    read(0, buffer, 50);
    myNewJSON = cJSON_Parse(buffer);
    
    /* adjusting setter parameters*/
    myNewXY = cJSON_GetObjectItem(myNewJSON, "myXY");
    xValJSON = cJSON_GetArrayItem(myNewXY, 0);

    /* setting the new (sin) value*/
    cJSON_SetNumberValue(myNewXY, sinf(cJSON_GetNumberValue(xValJSON)));

    /* printing the new values*/
    string = cJSON_Print(myNewJSON);
    write(1, string, strlen(string));
    
    /*avoiding stack overflow?*/
    free(string);
    cJSON_Delete(myNewJSON);
  }

  return 0;
}

As of now my code compiles, prints out the first value (cos(0)) and then gives me a core dumb and stops. Like this:

./cos_generator | ./receiver 
{
    "myXY": [{
            "x":    0,
            "y":    1
        }]
}Speicherzugriffsfehler (Speicherabzug geschrieben)

EDIT: My cos_generator.c code is:

/* includes*/
#include "cJSON.c"
#include <math.h>
#include <unistd.h>

#define MY_PI 3.141592
/* OutputData struct*/
struct OutputData {
  float x;
  float y;
};

/*parser-function*/
void parse_output_to_json(cJSON *pJson, struct OutputData *pOutput);

/*main function*/
int main() {

  cJSON *myJSON = cJSON_CreateObject(); /* Object is named 'myJSON'*/

  /* generator loop*/
  while (1) {
    static int counter = 0;
    float t_s = counter * 0.001f;
    float f = 1;

    struct OutputData current = {
        .x = t_s,
        .y = cosf(2 * MY_PI * f * t_s),
    };

    parse_output_to_json(myJSON, &current);

    char *string = cJSON_Print(myJSON);
    write(1, string, strlen(string));

    cJSON_free(string);

    usleep(1000);
    counter++;
  }

  cJSON_Delete(myJSON);

  /* Return val of main*/
  return 0;
}

void parse_output_to_json(cJSON *pJson, struct OutputData *pOutput) {
  /* Variables*/
  char *string = NULL; /* string to be written to*/
  cJSON *myXY = NULL;  // cJSON array
  size_t index = 0;

  /* Add Array 'myXY' to the myJSON*/
  myXY = cJSON_AddArrayToObject(pJson, "myXY");

  /* Add mySinXY to myNestedArray*/
  /* make array object inside the master object??*/
  cJSON *myNestedArray = cJSON_CreateObject();
  /* add numbers of mySinXY*/
  cJSON_AddNumberToObject(myNestedArray, "x", pOutput->x);
  cJSON_AddNumberToObject(myNestedArray, "y", pOutput->y);

  /* Add item 'myNestedArray' to myXY in myJSON*/
  cJSON_AddItemToArray(myXY, myNestedArray);
}

Solution

  • My code had multiple problems, e.g. a too short buffer length. While fixing all the syntactic and "stupid" errors, one problem remained:

    • It would not parse correctly

    Working on it for an hour with my colleague resulted in following enlightenment:

    Even when the nested Array only contains one element e.g. "x" containing only the integer "1", you would have to iterate over the "x". I didn't find a way to just access "x".

    This is exactly taken over from the example on the cJSON page.

    I put the parsing into a struct, this should work for a lot of use cases similarly:

    struct OutputData {
      float x;
      float y;
    };
    
    struct OutputData parseStruct(cJSON *json) {
      cJSON *myNewXY = NULL;
      cJSON *xValJSON = NULL;
      cJSON *yValJSON = NULL;
      cJSON *resolution = NULL;
      /* adjusting setter parameters*/
      myNewXY = cJSON_GetObjectItemCaseSensitive(json, "myXY");
    
      cJSON_ArrayForEach(resolution, myNewXY) {
        xValJSON = cJSON_GetObjectItemCaseSensitive(resolution, "x");
        yValJSON = cJSON_GetObjectItemCaseSensitive(resolution, "y");
      }
      struct OutputData result = {
          .x = (float)cJSON_GetNumberValue(xValJSON),
          .y = (float)cJSON_GetNumberValue(yValJSON),
      };
    
      return result;
    }