LiteOS Cloud Docking Tutorial 01-cJSON Component Use Tutorial

1. JSON and cJSON

JSON - Lightweight Data Format

JSON The full name JavaScript Object Notation, or the short spectrum of JS objects, is a lightweight data format.

It uses a text format completely independent of the programming language to store and represent data. Its syntax is simple, hierarchical structure is clear, easy to read and write, and it is also easy to parse and generate by machine, which effectively improves the efficiency of network transmission.

JSON Syntax Rules

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

  • Start with'{'and end with'}', allowing nested use;
  • Each name and value appear in pairs, separated by':';
  • Separate key-value pairs with ","
  • Allow meaningless whitespace around these characters;

For key values, you can have the following values:

  • A new json object
  • Array: using'['and']' to represent
  • Number: Directly expressed as either an integer or a floating point number
  • String: Use quotation marks to indicate
  • Literal value: one of false, null, true (must be lowercase)

Examples are as follows:

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

cJSON components in LiteOS

cJSON is a JSON data parser written in C language. It is ultra lightweight, portable and single file. It uses MIT open source protocol.

The cJSON has been ported to LiteOS and used as a component with only two source files in sdkIoT_LINK_1.0.0iot_linkcJSON:

  • cJSON.h
  • cJSON.c

To use this, simply copy the two files to the project directory and include the header file cJSON.h as follows:

#include "cJSON.h"

2. Data Structure and Design Ideas of cJSON

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

CJSON uses the cJSON structure to represent a JSON data, which is defined in cJSON.h with the following source code:

/* 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;

The cJSON is cleverly designed.

First, instead of abstracting a whole piece of JSON data, it abstracts one of the JSON data, which is a key-value pair, represented by the structure strcut cJSON above, where the list of members to store values is as follows:

  • String: The name used to represent the key-value pair;
  • Type: The type used to represent the median of the key value pair;
  • valuestring: If the key type is a string, point the pointer to the key value;
  • valueint: If the key type is an integer, point the pointer to the key value;
  • valuedouble: If the key type is a floating point number, point the pointer to the key value;

Secondly, a complete piece of JSON data consists of many key-value pairs and involves finding, deleting and adding key-value pairs, so a chain table is used to store the entire piece of JSON data, as shown in the code above:

  • Next pointer: points to the next key-value pair
  • prev pointer to previous key-value pair

Finally, because JSON data supports nesting, the value of a key-value pair is either a new JSON data object (a new chain table) or an array. For convenience, in cJSON, the array is also represented as an array object, which is stored in a chain table, so:

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

3. Open the cJSON component

In LiteOS, cJSON components are not turned on by default, and are turned on using the macro definition CONFIG_JSON_ENABLE.

Once turned on, LiteOS automatically initializes and uses the memory hook of cJSON to change the way cJSON requests memory to osalmalloc requests. The automatic initialization code is in the `linkmain.c` file:

4. JSON Data Encapsulation

Encapsulation method

The process of encapsulating JSON data is actually the process of creating a chain table and adding nodes to it.

First, let's talk about some terms in the list:

  • Head pointer: pointer to the head node of a chain list;
  • Head node: do not store valid data, easy chain table operation;
  • First node: the first node to store valid data;
  • End node: the last node to store valid data;

Having understood these concepts, we began to describe how to create a complete JSON data set, that is, a complete chain table.

  • (1) Create a header pointer:
 cJSON* cjson_test = NULL;

  • (2) Create a header node and point the header pointer at the header node:
cjson_test = cJSON_CreateObject();

  • (3) To add nodes to the list of chains:
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);

Output JSON Data

As mentioned above, a complete JSON data is a long list, so how do you print out this JSON data?

cJSON provides an API that outputs JSON information stored in the entire chain table to a string:

(char *) cJSON_Print(const cJSON *item);

When using it, you only need to receive the address of the pointer returned by the function.

Sample encapsulated and printed data

Simple narrative is not enough. Here is an example that encapsulates the JSON data given at the beginning.

First, based on the HelloWorld project, create a folder cloud_test_demo to store the sample files, and create a new experimental file cjson_print_demo.c. Write the following code:

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

static int cjson_print_demo_entry()
{
    cJSON* cjson_test = NULL;
    cJSON* cjson_address = NULL;
    cJSON* cjson_skill = NULL;
    char* str = NULL;

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

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

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

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

    /* Add a nested JSON data (add a linked 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 JSON data of an array type (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 JSON data of Boolean type with a value of False (add a list node) */
    cJSON_AddFalseToObject(cjson_test, "student");

    /* Print all data for JSON objects (entire list of chains) */
    str = cJSON_Print(cjson_test);
    printf("%s\n", str);

    /* Release entire chain table memory */
    cJSON_Delete(cjson_test);

    return 0;
}

int standard_app_demo_main()
{
    osal_task_create("cjson_print_demo",cjson_print_demo_entry,NULL,0x800,NULL,2);
    return 0;
}

Configuration file path in user_demo.mk:

    #example for cjson_print_demo
    ifeq ($(CONFIG_USER_DEMO), "cjson_print_demo")    
        user_demo_src  = ${wildcard $(TOP_DIR)/targets/STM32L431_BearPi/Demos/cloud_test_demo/cjson_print_demo.c}
    endif

The locations are as follows:

Then open the cJSON component in.sdkconfig and check the demo:

The experimental results are shown in Fig.

5. cJSON data parsing

analytic method

The process of parsing JSON data is essentially a process of peeling off a linked list node (key-value pair).

The parsing method is as follows:

  • (1) Create a chain header pointer:
cJSON* cjson_test = NULL;

  • (2) Parse the whole JSON data and return the address of the chain header node to assign to the header pointer:

There is only one API for parsing the entire piece of data:

(cJSON *) cJSON_Parse(const char *value);

  • (3) Return the address of the key-value pair (link list node) by taking the corresponding value from the list according to the name of the key-value pair
(cJSON *) cJSON_GetObjectItem(const cJSON * const object, const char * const string);

  • (4) If the value of JSON data is an array, use the following two API s to extract data:
(int) cJSON_GetArraySize(const cJSON *array);
(cJSON *) cJSON_GetArrayItem(const cJSON *array, int index);

Parse example

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

In the folder cloud_test_demo where the sample files are stored, create a new experimental file, cjson_parse_demo.c, and write the following code:

#include <osal.h>
#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\
}";

static int cjson_test1_demo_entry()
{
   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 entire JSO data */
    cjson_test = cJSON_Parse(message);
    if(cjson_test == NULL)
    {
        printf("parse fail.\n");
        return -1;
    }

    /* Extract JSON data (key-value pairs) by name in turn */
    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);

    /* Parsing 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 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");

    /* Parsing Boolean Data */
    cjson_student = cJSON_GetObjectItem(cjson_test, "student");
    if(cjson_student->valueint == 0)
    {
        printf("student: false\n");
    }
    else
    {
        printf("student:error\n");
    }

    /* Release entire chain table memory */
    cJSON_Delete(cjson_test);
    
    return 0;
}

int standard_app_demo_main()
{
    osal_task_create("cjson_test1_demo",cjson_test1_demo_entry,NULL,0x800,NULL,2);
    return 0;
}

Configuration file path in user_demo.mk:

    #example for cjson_parse_demo
    ifeq ($(CONFIG_USER_DEMO), "cjson_parse_demo")    
        user_demo_src  = ${wildcard $(TOP_DIR)/targets/STM32L431_BearPi/Demos/cloud_test_demo/cjson_parse_demo.c}
    endif

The locations are as follows:

Then open the cJSON component in.sdkconfig and check the demo:

The experimental results are shown in Fig.

Matters needing attention

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

6. Memory issues during cJSON use

Memory released in time

All operations of cJSON are based on a chain table, so cJSON uses malloc to allocate dynamic memory from the heap a lot during use, so after use, the following functions should be called in time to empty the memory pointed to by the cJSON pointer, which can also be used to delete a piece of data:

(void) cJSON_Delete(cJSON *item);

Note: When the function deletes a JSON data, if there is a nesting, it is deleted concurrently.

Memory hook

cJSON supports custom malloc and free functions as follows:

  • (1) Use cJSON_Hooks to connect custom malloc functions to 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;

  • (2) Initialization hook cJSON_Hooks
(void) cJSON_InitHooks(cJSON_Hooks* hooks);
Nineteen original articles were published, 0 were praised, and 1446 were visited
Private letter follow

Tags: JSON Java Python Javascript

Posted on Thu, 06 Feb 2020 20:57:43 -0800 by Minase