forkachild / C-Simple-JSON-Parser

Extremely lightweight, easy-to-use & blazing fast JSON parsing library written in pure C
https://forkachild.github.io/C-Simple-JSON-Parser/
MIT License
45 stars 16 forks source link

Parsing int values and nested elements #19

Closed mailow-vsl closed 11 months ago

mailow-vsl commented 11 months ago

Hi @forkachild , I am trying to use your library in a project with STM32 microprocessor. It works like a charm, but only for string values. How I can fetch int value of the elements? I need to store the value in other variables to work with them and store them to EEPROM memory.

Eg how to fetch int values when parsing {"level":15, "temperature":125} to store them like int?

forkachild commented 11 months ago

@mailow-vsl Notice the whitespace between the two key-value pairs. To accomodate this, you need to either:

  1. #define JSON_SKIP_WHITESPACE before #include "json.h", or
  2. Compile with -DJSON_SKIP_WHITESPACE

Note: This would incur a small overhead to skip the whitespaces.

After parsing, get the integer value by (int) element.value.as_number.value.as_long. See example below for your usecase:

const char *json = "{\"level\":15, \"temperature\":125}";

result(json_element) element_result = json_parse(json);

if (result_is_err(json_element)(&element_result)) {
  typed(json_error) error = result_unwrap_err(json_element)(&element_result);
  fprintf(stderr, "Error parsing JSON: %s\n", json_error_to_string(error));
  return -1;
}

typed(json_element) element = result_unwrap(json_element)(&element_result);

if (element.type == JSON_ELEMENT_TYPE_OBJECT) {
  result(json_element) r_temp =
      json_object_find(element.value.as_object, "temperature");
  if (result_is_ok(json_element)(&r_temp)) {
    typed(json_element) temp = result_unwrap(json_element)(&r_temp);
    printf("Temperature is %d\n", (int)temp.value.as_number.value.as_long);
  }
}

json_free(&element);

Hope this solves your issue

mailow-vsl commented 11 months ago

Oh, excelent, that worked. Thank you, sending beers ;)

mailow-vsl commented 11 months ago

Please may I have one more question @forkachild? How I can fetch nested elements? Like this

const char * UART_RX_PC_Buffer = "{\"confMode\":1,\"morning\":{\"over\":1,\"start\":\"10:30\",\"end\":\"16:00\"},\"crc\":123456}";
int hour, minute;
int confMode, crc; 
int LANOverEco, LANTimeStartEco, LANTimeEndEco;
int main()
{
result(json_element) element_result = json_parse(UART_RX_PC_Buffer);

    // Guard if
    if(result_is_err(json_element)(&element_result)) return -1;

    // Extract the data
    typed(json_element) element = result_unwrap(json_element)(&element_result);

    // Fetch the "confMode" key value .. and others
    if (element.type == JSON_ELEMENT_TYPE_OBJECT) {
      result(json_element) r_confMode = json_object_find(element.value.as_object, "confMode");
      if (result_is_ok(json_element)(&r_confMode)) {
        typed(json_element) t_confMode = result_unwrap(json_element)(&r_confMode);
        confMode = (int)t_confMode.value.as_number.value.as_long;
      }
    }

    switch(confMode) {

    case 1: //ECO
        if (element.type == JSON_ELEMENT_TYPE_OBJECT) {
          result(json_element) r_start = json_object_find(element.value.as_object, "start");
          if (result_is_ok(json_element)(&r_start)) {
            typed(json_element) t_start = result_unwrap(json_element)(&r_start);
            if (sscanf(t_start.value.as_string, "%d:%d", &hour, &minute) == 2) LANTimeStartEco = hour * 60 + minute;
          }
        }
        if (element.type == JSON_ELEMENT_TYPE_OBJECT) {
          result(json_element) r_end = json_object_find(element.value.as_object, "end");
          if (result_is_ok(json_element)(&r_end)) {
            typed(json_element) t_end = result_unwrap(json_element)(&r_end);
            if (sscanf(t_end.value.as_string, "%d:%d", &hour, &minute) == 2) LANTimeEndEco = hour * 60 + minute;
          }
        }
        if (element.type == JSON_ELEMENT_TYPE_OBJECT) {
          result(json_element) r_over = json_object_find(element.value.as_object, "over");
          if (result_is_ok(json_element)(&r_over)) {
            typed(json_element) t_over = result_unwrap(json_element)(&r_over);
            LANOverEco = (int)t_over.value.as_number.value.as_long;
          }
        }
        if (element.type == JSON_ELEMENT_TYPE_OBJECT) {
          result(json_element) r_crc = json_object_find(element.value.as_object, "crc");
          if (result_is_ok(json_element)(&r_crc)) {
            typed(json_element) t_crc = result_unwrap(json_element)(&r_crc);
            crc = (int)t_crc.value.as_number.value.as_long;
          }
        }
    break;

    case 2: //AUTO
    // code block
    break;
    }
json_print(&element, 1);
printf("\n\nconfMode: %d, start: %d, end: %d, over: %d, crc: %d\n", confMode, LANTimeStartEco, LANTimeEndEco,LANOverEco, crc);
return 0;
}

So here I tried to find the key "over" (some flag) and "start" and "end" keys and count minutes after midnight. Am I doing it wrong?

I can see that structure is parsed correctly.

{
 "confMode": 1,
 "morning": {
  "start": "10:30",
  "end": "16:00",
  "over": 1
 },
 "crc": 123456
}

But my values in my output printf are not.

confMode: 1, start: 0, end: 0, over: 0, crc: 123456
mailow-vsl commented 11 months ago

I suppose I miss something, because in my next structure I need to separate individual times during the day which are nested in day phases :)

{
  "confMode": 2,
  "morning": {
    "over": 1,
    "start": "7:30",
    "end": "9:00"
  },
  "afternoon": {
    "over": 1,
    "start": "15:00",
    "end": "19:00"
  },
  "duringday": {
    "over": 0,
    "start": "9:00",
    "end": "15:00"
  },
  "crc": 12345
}
forkachild commented 11 months ago

@mailow-vsl Yes, you need to parse the "morning", "afternoon" & "duringday" level elements first and then parse values from inside those objects.

What I have noticed is, parsing each level like this requires writing a lot of boilerplate. I was thinking about a MACRO like

#define result_guard_else_return(type, resval, varname, retval)                \
  result(type) r_##varname = resval;                                           \
  if (result_is_err(type)(&r_##varname))                                       \
    return retval;                                                             \
  typed(type) varname = result_unwrap(type)(&r_##varname);

Note: This is just a rough idea

This can result in simplified statements like

result_guard_else_return(json_element, json_parse(UART_RX_PC_Buffer), root, -1);
result_guard_else_return(json_element, json_object_find(root.value.as_object, "confMode"), t_confMode, -1);
confMode = (int) t_confMode.value.as_number.value.as_long;

What is your opinion? I'm open to ideas.

mailow-vsl commented 11 months ago

Ok, seems that if I parse it like you said, it works. it's a bit of extra work, but I'm fine with that so far ;)

Thx for your help and advice and for your amazing lib :P