1. The JSON and cJSON

JSON – Lightweight data format

JSON is a lightweight data format that provides JavaScript Object Notation.

It uses text format completely independent of programming language to store and express data, with concise syntax and clear hierarchical structure, which is easy for people to read and write, and also easy for machines to parse and generate, effectively improving the efficiency of network transmission.

JSON syntax rules

The JSON object is an unordered collection of name/value key-value pairs:

  • In order to”{To begin with}“End, allow nested use;
  • Each name and value comes in a pair.”:“Separation;
  • Between key and value pairs.“Separate
  • Allow meaningless whitespace before and after these characters;

For keys, you can have the following values:

  • A new JSON object
  • Array: use”[“And”]“Said
  • Numbers: Direct representation, which can be an integer or a floating point number
  • String: Use quotation marks"said
  • Literals: one of false, null, or true (must be lowercase)

The following is an example:

{
    "name": "mculover666"."age": 22."weight": 55.5
    "address":
    {
        "country": "China"."zip-code": 111111
    },
    "skill": ["c"."Java"."Python"]."student": false
}
Copy the code

cJSON

CJSON is a JSON data parser written in C language, with ultra-lightweight, portable, single-file features, using MIT open source protocol.

The cJSON project is hosted on Github with the following repository address:

Github.com/DaveGamble/…

Use the Git command to pull it locally:

git clone https://github.com/DaveGamble/cJSON.git
Copy the code

After pulling the cJSON source from Github, there are many files, but there are only two cJSON source files:

  • cJSON.h
  • cJSON.c

To use it, just copy the two files to the project directory and include the header cjson.h as follows:

#include "cJSON.h"
Copy the code

2. CJSON data structure and design idea

The design philosophy of cJSON is reflected in its data structure.

CJSON uses a cJSON structure to represent a JSON data, defined in cjson. h, source code as follows:

/* The cJSON structure: */
typedef struct cJSON
{
    /* next/prev allow you to walk array/object chains. Alternatively, use GetArraySize/GetArrayItem/GetObjectItem */
    struct cJSON *next;
    struct cJSON *prev;
    /* An array or object item will have a child pointer pointing to a chain of the items in the array/object. */
    struct cJSON *child;

    /* The type of the item, as above. */
    int type;

    /* The item's string, if type==cJSON_String and type == cJSON_Raw */
    char *valuestring;
    /* writing to valueint is DEPRECATED, use cJSON_SetNumberValue instead */
    int valueint;
    /* The item's number, if type==cJSON_Number */
    double valuedouble;

    /* The item's name string, if this item is the child of, or is in the list of subitems of an object. */
    char *string;
} cJSON;
Copy the code

CJSON is cleverly designed.

First, instead of abstracting the entire JSON data, it abstracts a single piece of JSON data, a key-value pair, represented by the structure strcut cJSON above, which holds the following list of values:

  • String: The name used to represent the key-value pair;
  • type: Used to represent the type of the key-value pair median;
  • valuestringPointer to key if type is a string;
  • valueintPointer to key if type is an integer;
  • valuedoublePointer to a key if type is a floating-point number;

Second, a complete PIECE of JSON data consists of many key-value pairs, and it involves finding, deleting, and adding key-value pairs. Therefore, a linked list is used to store the entire piece of JSON data, as shown in the code above:

  • nextPointer: Points to the next key/value pair
  • prevThe pointer points to the last key-value pair

Finally, because JSON data supports nesting, the value of a key-value pair can be a new JSON data object (a new linked list), or possibly an array. For convenience, arrays in cJSON are also represented as array objects, stored in a linked list, so:

In a key-value pair structure, when the value of the key-value pair is nested JSON data or an array, the child pointer points to the new list.

3. Encapsulate JSON data

Encapsulation method

The process of encapsulating JSON data is essentially the process of creating and adding nodes to a linked list.

Let’s start with some of the terminology in linked lists:

  • Header pointer: a pointer to the header of a linked list;
  • Header node: does not store valid data, convenient linked list operation;
  • Primary node: the first node where valid data is stored;
  • Tail node: The last node where valid data is stored;

With these concepts in mind, let’s move on to creating a complete piece of JSON data, that is, creating a complete linked list.

  • Create a header pointer:
 cJSON* cjson_test = NULL;
Copy the code
  • Create a header and set the pointer to the header.
cjson_test = cJSON_CreateObject();
Copy the code
  • ③ Add nodes to the list as much as you like:
cJSON_AddNullToObject(cJSON * const object, const char * const name);

cJSON_AddTrueToObject(cJSON * const object, const char * const name);

cJSON_AddFalseToObject(cJSON * const object, const char * const name);

cJSON_AddBoolToObject(cJSON * const object, const char * const name, const cJSON_bool boolean);

cJSON_AddNumberToObject(cJSON * const object, const char * const name, const double number);

cJSON_AddStringToObject(cJSON * const object, const char * const name, const char * const string);

cJSON_AddRawToObject(cJSON * const object, const char * const name, const char * const raw);

cJSON_AddObjectToObject(cJSON * const object, const char * const name);

cJSON_AddArrayToObject(cJSON * const object, const char * const name);
Copy the code

Output JSON data

How do you print out a long list of JSON data?

CJSON provides an API to output the JSON information stored in the entire list to a string:

(char *) cJSON_Print(const cJSON *item);
Copy the code

When used, only the address of the pointer returned by the function is received.

Encapsulate data and print data examples

This is not enough. Here is an example that encapsulates the JSON data given at the beginning:

#include <stdio.h>
#include "cJSON.h"

int main(void)
{
    cJSON* cjson_test = NULL;
    cJSON* cjson_address = NULL;
    cJSON* cjson_skill = NULL;
    char* str = NULL;

    /* Create a JSON data object (list header) */
    cjson_test = cJSON_CreateObject();

    /* Add a string of JSON data (add a list node) */
    cJSON_AddStringToObject(cjson_test, "name"."mculover666");

    /* Add an integer of JSON data (add a list node) */
    cJSON_AddNumberToObject(cjson_test, "age".22);

    /* Add a floating point JSON data (add a list node) */
    cJSON_AddNumberToObject(cjson_test, "weight".55.5);

    /* Add a nested JSON data (add a list node) */
    cjson_address = cJSON_CreateObject();
    cJSON_AddStringToObject(cjson_address, "country"."China");
    cJSON_AddNumberToObject(cjson_address, "zip-code".111111);
    cJSON_AddItemToObject(cjson_test, "address", cjson_address);

    /* Add an array of JSON data (add a list node) */
    cjson_skill = cJSON_CreateArray();
    cJSON_AddItemToArray(cjson_skill, cJSON_CreateString( "C" ));
    cJSON_AddItemToArray(cjson_skill, cJSON_CreateString( "Java" ));
    cJSON_AddItemToArray(cjson_skill, cJSON_CreateString( "Python" ));
    cJSON_AddItemToObject(cjson_test, "skill", cjson_skill);

    /* Add a Boolean JSON data value of False (add a list node) */
    cJSON_AddFalseToObject(cjson_test, "student");

    /* Prints all the data of the JSON object (the whole list) */
    str = cJSON_Print(cjson_test);
    printf("%s\n", str);

    return 0;
}
Copy the code

Compile and run:

gcc cJSON.c example1.c -o example1.exe
Copy the code

The experimental results are shown as follows:

The relationship of the JSON data link list is shown as follows:

4. CJSON data parsing

Analytical methods

The process of parsing JSON data is essentially the process of stripping out linked list nodes (key-value pairs) one by one.

The analytical method is as follows:

  • Create table header pointer
cJSON* cjson_test = NULL;
Copy the code
  • (2) Parse the entire JSON data, return the address of the linked list header, and assign it to the header pointer:

There is only one API to parse the entire data:

(cJSON *) cJSON_Parse(const char *value);
Copy the code
  • ③ Fetch the value from the linked list according to the name of the key-value pair, and return the address of the key-value pair (linked list node)
(cJSON *) cJSON_GetObjectItem(const cJSON * const object, const char * const string);
Copy the code
  • (4) If the value of JSON data is an array, use the following two apis to extract data:
(int) cJSON_GetArraySize(const cJSON *array);
(cJSON *) cJSON_GetArrayItem(const cJSON *array.int index);
Copy the code

Parsing example

Here is an example of how to parse the JSON data given at the beginning:

#include <stdio.h>
#include "cJSON.h"

char *message = 
"{\ \" name \ ": \" McUlover666 \ ", \ \ "age \" : 22, \ \ "weight \" : 55.5, \ \ "address \" : \ {\ \ "country \" : \"China\",\ \"zip-code\": 111111\ }, \ \"skill\": [\"c\", \"Java\", \"Python\"],\ \"student\": false \ }";

int main(void)
{
    cJSON* cjson_test = NULL;
    cJSON* cjson_name = NULL;
    cJSON* cjson_age = NULL;
    cJSON* cjson_weight = NULL;
    cJSON* cjson_address = NULL;
    cJSON* cjson_address_country = NULL;
    cJSON* cjson_address_zipcode = NULL;
    cJSON* cjson_skill = NULL;
    cJSON* cjson_student = NULL;
    int    skill_array_size = 0, i = 0;
    cJSON* cjson_skill_item = NULL;

    /* Parse the entire JSO data */
    cjson_test = cJSON_Parse(message);
    if(cjson_test == NULL)
    {
        printf("parse fail.\n");
        return - 1;
    }

    /* Extract JSON data by name (key-value pairs) */
    cjson_name = cJSON_GetObjectItem(cjson_test, "name");
    cjson_age = cJSON_GetObjectItem(cjson_test, "age");
    cjson_weight = cJSON_GetObjectItem(cjson_test, "weight");

    printf("name: %s\n", cjson_name->valuestring);
    printf("age:%d\n", cjson_age->valueint);
    printf("weight:%.1f\n", cjson_weight->valuedouble);

    /* Parse nested JSON data */
    cjson_address = cJSON_GetObjectItem(cjson_test, "address");
    cjson_address_country = cJSON_GetObjectItem(cjson_address, "country");
    cjson_address_zipcode = cJSON_GetObjectItem(cjson_address, "zip-code");
    printf("address-country:%s\naddress-zipcode:%d\n", cjson_address_country->valuestring, cjson_address_zipcode->valueint);

    /* Parse the array */
    cjson_skill = cJSON_GetObjectItem(cjson_test, "skill");
    skill_array_size = cJSON_GetArraySize(cjson_skill);
    printf("skill:[");
    for(i = 0; i < skill_array_size; i++)
    {
        cjson_skill_item = cJSON_GetArrayItem(cjson_skill, i);
        printf("%s,", cjson_skill_item->valuestring);
    }
    printf("\b]\n");

    /* Parse Boolean data */
    cjson_student = cJSON_GetObjectItem(cjson_test, "student");
    if(cjson_student->valueint == 0)
    {
        printf("student: false\n");
    }
    else
    {
        printf("student:error\n");
    }
    
    return 0;
}
Copy the code

Compile:

gcc cJSON.c example2.c -o example2.exe
Copy the code

The running results are shown as follows:

Matters needing attention

In this example, because I know the data type in advance, such as character type or floating point type, I directly use the pointer to the corresponding data field to extract. In actual use, if the data type is not determined in advance, I should first judge the value of type to determine the data type, and then extract the data from the corresponding data field.

5. Memory problems during cJSON usage

Timely memory release

All cJSON operations are based on linked lists, so cJSON uses malloc to allocate dynamic memory from the heap. Therefore, after using cJSON, we should call the following function to clear the memory pointed by the cJSON pointer. This function can also be used to delete a certain item of data:

(void) cJSON_Delete(cJSON *item);
Copy the code

Note: This function deletes a piece of JSON data, if nested, along with it.

Memory hook

CJSON supports custom malloc and free functions as follows:

  • (1) usingcJSON_HooksTo connect the custom malloc and free functions:
typedef struct cJSON_Hooks
{
      /* malloc/free are CDECL on Windows regardless of the default calling convention of the compiler, so ensure the hooks allow passing those functions directly. */
      void *(CJSON_CDECL *malloc_fn)(size_t sz);
      void (CJSON_CDECL *free_fn)(void *ptr);
} cJSON_Hooks;
Copy the code
  • ② Initialize cJSON_Hooks
(void) cJSON_InitHooks(cJSON_Hooks* hooks);
Copy the code