【Detailed Explanation】cJSON Analysis

Table of contents

1. Get a preliminary understanding of cJSON through the README file:

1.1. The beginning and end of the header file:

1.2. The macro definition of the cJSON type in the header file

1.3, extern in the header file

2. Read and analyze cJSON source code

2.1. Structure struct cJSON (algorithm design idea):

2.2. Structure stuct cJSON_Hooks:

2.3, cJSON_InitHooks function

2.4, cJSON_New_Item function

2.5, cJSON_Delete function

2.6, parse_number function

2.6, pow2gt function

2.8, update function

2.9, print_number function

2.1.1, parse_hex4 function

2.1.2, parse_string function

2.1.3, print_string_ptr function

2.1.4, cJSON_ParseWithOpts function

2.1.5, parse_array function

2.1.6, parse_object function

2.1.7, print_object function

2.1.8, print_array function

3. cJSON instance

3.1, test.c test code in the source package

3.2. Use cases


1. Get a preliminary understanding of cJSON through the README file:

Judging from the introduction of the content of the README file, cJSON is a JSON data parser written in C language. It is portable, portable, and an easy-to-use parser. It has only two cJson source code files: a C file (cJSON.c) and a header file (cJSON.h). Because of its simplicity and ease of use, its GitHub has attracted many programmers around the world to contribute code based on it. You can use the git clone https://github.com/DaveGamble/cJSON.git statement to copy to the local, or go to the cJSON download | SourceForge.net website to download to the local.

1.1. The beginning and end of the header file:

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5bGx5pyq5a2k44GG,size_20,color_FFFFFF,t_70,g_se,x_16

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5bGx5pyq5a2k44GG,size_20,color_FFFFFF,t_70,g_se,x_16  

#ifndef cJSON_h,#define cJSON_h,#endif . This is to prevent header files from being quoted repeatedly.

The main function of extern "C" is to correctly implement C++ code to call other C language code. After adding extern "C", it will instruct the compiler to compile this part of the code as C language, not C++. Since C++ supports function overloading, the compiler will add the parameter types of the function to the compiled code during the process of compiling the function, not just the function name; and the C language does not support function overloading, so compiling C The function of the language code will not bring the parameter type of the function, generally including the function name.

1.2. The macro definition of the cJSON type in the header file

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5bGx5pyq5a2k44GG,size_20,color_FFFFFF,t_70,g_se,x_16

These macro definitions are the value definitions of the structure type. When processing, you only need to perform bit operations on the value of type &255 to get the data type stored in json.

1.3, extern in the header file

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5bGx5pyq5a2k44GG,size_20,color_FFFFFF,t_70,g_se,x_16

extern is a keyword in C language. It is generally used before variable names or function names. It is used to explain "this variable/function is defined elsewhere and should be referenced here."

2. Read and analyze cJSON source code

2.1. Structure struct cJSON (algorithm design idea):

In the header file (cJSON.h):

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5bGx5pyq5a2k44GG,size_20,color_FFFFFF,t_70,g_se,x_16

cJSON uses a structure to represent a JSON data. It does not abstract a whole section of JSON data, but abstracts a piece of JSON data.

The storage structure of cjson is like a generalized table, in fact, it can also be said to be a tree, but sibling nodes are connected by two pointers, prev and next.

prev and next are the predecessor and successor of the cjson object respectively, belonging to the same level of objects. chid points to the child node and is the pointer to the first child.

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5bGx5pyq5a2k44GG,size_20,color_FFFFFF,t_70,g_se,x_16

analyze:

  1. Next pointer: used to point to the next key-value pair

Prev pointer: used to point to the previous key-value pair

JSON is a piece of data, and the structure stores one piece of data. In order to traverse, search, delete, and add the entire piece of data, cJSON uses a linked list for storage.

2. Child pointer: used to point to a new linked list

3. String: Indicates the data name of the JSON;

type: Indicates the data type of the JSON;

valuestring: If type is a string type, point the pointer to the piece of data, indicating the value of the data;

valueint: If type is an integer, point the pointer to the piece of data, indicating the value of the data;

valuedouble: If the type is a floating-point number, point the pointer to the piece of data, indicating the value of the data;

The structure defines a series of member variables to store values ​​in the form of a key-value pair, which is concise and beautiful.

2.2. Structure stuct cJSON_Hooks:

In the header file (cJSON.h):

833d7584a5384d8cb307ecf4c3ccdfac.png

Analysis: The malloc function and the free function are defined. The malloc function is actually in the memory: find a space of a specified size, and then give the first address of this space to a pointer variable. The pointer variable here can be a single pointer or It is the first address of an array, which depends on the specific content of the parameter size in the malloc function. The Free function is to return the space allocated by malloc to the program or the operating system, that is, to release this memory and let it be free again. Through the structure struct cJSON_Hooks, it is linked with the memory allocation of internal calls.

2.3, cJSON_InitHooks function

In the C file (cJSON.c):

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5bGx5pyq5a2k44GG,size_20,color_FFFFFF,t_70,g_se,x_16

In the cJSON project, there is cJSON_InitHooks() function, which is the Hooks memory management function.

analyze:

  1. The if statement indicates that if the hooks are empty, the default memory management will be used and then returned.
  2. You can also customize the memory management function. The ternary operator is used here, combined with the function pointer. If customized

malloc function and free function, the pointer points to the custom function, otherwise it points to the default function.

2.4, cJSON_New_Item function

In the C file (cJSON.c):

cc444b9c1bff4879a1baedab9835369f.png

It not only initializes the memory, but also returns a cJSON node.

analyze:

  1. Define a node as the return value, and node points to a new cJSON node. malloc out a node, malloc function can allocate space to it.
  2. The If statement, if the node is successfully malloced, the memset function is called to initialize the memory to 0. The memset function is a function in the standard library <string.h>.
  3. Return node, the return type is (cJSON*) type.

2.5, cJSON_Delete function

In the C file (cJSON.c):

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5bGx5pyq5a2k44GG,size_20,color_FFFFFF,t_70,g_se,x_16

It can be used to delete a whole JSON data, and release memory of all nodes at the same time.

analyze:

  1. Pass in a whole cJSON type data, pointing to the pointer c.
  2. Define a next pointer of cJSON class for recursion.
  3. If the cJSON node pointed to by the current pointer c is not empty, enter the loop body to perform the delete operation.
  4. First define next to save the next pointer pointing position of the current pointer c for later recursive traversal c=next
  5. If the cJSON structure is passed in, and c->child, call the cJSON_Delete function to delete the nested child list.
  6. If the input is a cJSON structure, and c->valuestring, then call the cJSON_free function to release the node memory.
  7. If the cJSON structure is passed in, and c->string, call the cJSON_free function to release the node memory.

2.6, parse_number function

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5bGx5pyq5a2k44GG,size_20,color_FFFFFF,t_70,g_se,x_16

The Parse_number function can convert an array of numeric characters into a basic data type and assign it to a piece of cJSON data, and return an array of numeric characters.

analyze:

  1. The first if judges whether it is positive or negative.
  2. The second if judges whether the current num point is 0
  3. The third if means that if the current num points to 1~9, the character will be parsed into an int type (*num++-'0'), and at the same time, the num pointer will be incremented to point to the next character. Through this loop body, it is used to parse the string into an int type and store it in n.
  4. The fourth if means that if the current num points to ".", there is a decimal part, and the next character of the decimal is a legal numeric character, the decimal part is solved, and it is still a loop body, which is used to parse the string It is of type int and stored in n. At the same time, the scale is decremented, indicating the number of decimal places. Scale=-1, means there is 1 digit after the decimal point.
  5. The fifth if means that if the current num points to "e" or "E", that is, there is an index, solve the index part, first judge the next character pointed to by the current num self-increment, if the next character is "+", continue Self-increment, else if the next character is "-", then set signsubscale to -1, indicating that the exponent is negative and continue to self-increment. If the next one points to a legal numeric character, enter the loop body, parse the string into an int type, and save it in subscale.
  6. sign represents the positive or negative of the data, n represents the integer digits and decimal digits of the data, call the pow function in math, scale represents the decimal digits, subscale represents the exponent digits, and signsubscale represents the positive or negative exponent digits. save the final result in n
  7. Assign n to valuedouble and valueint in cJSON data, and set the type to cJSON_Number

2.6, pow2gt function

fb923f6ac22d4576abc8702d94594056.png

The function of this function is to return the smallest 2 to the Nth power greater than x.

analyze:

  1. The int type occupies 4 bytes in C language, 32 bits
  2. for example:

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5bGx5pyq5a2k44GG,size_17,color_FFFFFF,t_70,g_se,x_16

  1. For any M, when it executes the 5-step operation in the above figure, the leftmost bit of M that is 1 is assigned a value of 1 to the right, so when it returns x+1 at the end, it will return >= M is the smallest 2 to the Nth power. The initial –x is because when x itself is a 2 to the Nth power, the return value is x itself, so it needs to be subtracted by 1 to ensure correctness.

2.7, structure struct printbuffer and ensure function

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5bGx5pyq5a2k44GG,size_20,color_FFFFFF,t_70,g_se,x_16

A series of memory-related variables are defined in the structure printbuffer. The ensure function is used to allocate space, ensure that the memory is sufficient, and return an available memory pointer. Before assigning a value to the printbuffer each time, it is necessary to calculate the required memory and call ensure() to ensure that the memory is sufficient.

analyze:

  1. If the current printbuffer pointer points to NULL or the internal variable buffer is NULL, then return 0, indicating that memory cannot be allocated.
  2. needed indicates the required memory size, initialization, needed plus offset offset
  3. If the required memory is sufficient, return the starting point of available memory
  4. If it is not enough, call the pow2gt function to allocate a new memory size. In order to allocate reasonably, set a newsize to save the minimum 2 N times larger than the required memory
  5. According to the size of newsize, call cJSON_malloc to open up a new space.
  6. If no new space can be created, call cJSON_free to release the printbuffer memory, and return 0, indicating that memory cannot be allocated.
  7. If the new space is successfully opened, call the memcpy() function in the standard library <string.h> to copy length bytes from the storage area buffer to the new storage area (newbuffer).
  8. Then release the memory of the buffer.
  9. Install a new buffer and length for p

10. Finally, return the starting point of the newly allocated available memory

2.8, update function

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5bGx5pyq5a2k44GG,size_20,color_FFFFFF,t_70,g_se,x_16

The update function is used to update the offset offset in the printbuffer and return the updated offset

analyze:

  1. define pointer str
  2. If the current p pointer is empty or the internal variable buffer of the p pointer is empty, return 0
  3. Otherwise, the str pointer points to the starting point of the memory address buffer+offset
  4. Finally, return the updated offset offset.

2.9, print_number function

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5bGx5pyq5a2k44GG,size_20,color_FFFFFF,t_70,g_se,x_16

print number function

analyze:

  1. Define a string as the return value.
  2. Define a floating-point type d to save the valuedouble value in the incoming cJSON data.
  3. The first if: If d is 0 and p is not empty, ensure that the memory required by p is sufficient. If p is not empty, apply for a new space with a size of 2 bytes. If str is not empty, call the strcpy function in the standard library - <string.h> to copy "0" to str.
  4. The first else if: DBL_EPSILON means the smallest positive number INT_MAX, and INT_MIN means the largest int value and the smallest int value. This is equivalent to judging whether an int type positive integer is stored in the item.

If yes, and p is not empty, ensure that the memory required by p is sufficient, and if p is not empty, apply for a new space with a size of 21 bytes. If str is not empty, call the sprintf function to write the formatted data into the string str. So the meaning here is, write the item->valueint number into the str string.

  1. The latter else statement: It is equivalent to judging whether the item is stored in a floating-point type. If yes, and p is not empty, ensure that the memory required by p is sufficient. If p is not empty, apply for a new space with a size of 64. If str is not empty, call the sprintf function according to different precisions, and write the formatted data into the string str. The floor function indicates rounding down. %.0f means that the length of the data is not specified, and 0 digits are reserved after the decimal point, that is, the decimal part is not reserved. %e means to output in scientific notation, and %f means to output a value of float type.

2.1.1, parse_hex4 function

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5bGx5pyq5a2k44GG,size_20,color_FFFFFF,t_70,g_se,x_16

This function can convert a four-digit hexadecimal number into a decimal number.

2.1.2, parse_string function

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5bGx5pyq5a2k44GG,size_20,color_FFFFFF,t_70,g_se,x_16

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5bGx5pyq5a2k44GG,size_20,color_FFFFFF,t_70,g_se,x_16

This function can parse a string from a string. When parsing, it will convert it into a real carriage return and line feed, rather than a simple string copy.

2.1.3, print_string_ptr function

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5bGx5pyq5a2k44GG,size_20,color_FFFFFF,t_70,g_se,x_16

Analyze the unsatisfied conditions, that is, when the flag is 1. That is, the part of the condition in the yellow box above is true, that is, the first character of the string can be a double quote or backslash or the ASCII code is less than 32. Let's first look at the ASCII codes less than 32 include What content, as shown below:

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5bGx5pyq5a2k44GG,size_20,color_FFFFFF,t_70,g_se,x_16

It can be seen that there are some control strings, non-display strings, control characters are used to achieve specific operations, such as some commonly used control characters are ESC, BACKSPACE (delete the previous character), Del (delete the current character) , carriage return, line feed, etc. First judge whether it is an empty string, if it is, directly output two double quotation marks.

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5bGx5pyq5a2k44GG,size_20,color_FFFFFF,t_70,g_se,x_16

Next, let's analyze the last while() part. This part is actually easy to understand, that is, it will be saved directly if the character is displayed, otherwise it needs to be judged and some backslashes should be added. It should be noted that the last default statement, the sprintf() function and the printf() function are similar, adding an s in front means a string, the difference is that printf() writes the content on the window, and the sprintf() function It is to write the content into its first parameter, which refers to ptr2 here. And "ux" refers to writing in 4-digit hexadecimal, 0 means that if it is less than 4, fill in 0, and 4 means The meaning of 4 bits, x means hexadecimal.

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5bGx5pyq5a2k44GG,size_20,color_FFFFFF,t_70,g_se,x_16

The print_string function internally calls print_String_ptr, which can print a specific cJSON data

2424908b19bc455492c32e4e548633b7.png

2.1.4, cJSON_ParseWithOpts function

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5bGx5pyq5a2k44GG,size_20,color_FFFFFF,t_70,g_se,x_16

ParseWithOpts allows JSON to be null terminated and retrieves a pointer to the last byte parsed. If a ptr is provided in return_parse_end, but parsing fails, then return_parse_end will contain a pointer to error, and thus will match cJSON_GetErrorPtr(). The cJSON_parse function internally calls the cJSON_ParseWithOpts function.

2.1.5, parse_array function

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5bGx5pyq5a2k44GG,size_20,color_FFFFFF,t_70,g_se,x_16

This function can parse a string into an array. When parsing an array, the basic idea is to recursively call parse_value for each element in the array, and then connect these elements to form a linked list.

2.1.6, parse_object function

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5bGx5pyq5a2k44GG,size_20,color_FFFFFF,t_70,g_se,x_16

 watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5bGx5pyq5a2k44GG,size_20,color_FFFFFF,t_70,g_se,x_16

This function can parse a string into an object.

2.1.7, print_object function

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5bGx5pyq5a2k44GG,size_20,color_FFFFFF,t_70,g_se,x_16

This function can be used to print JSON objects.

2.1.8, print_array function

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5bGx5pyq5a2k44GG,size_20,color_FFFFFF,t_70,g_se,x_16

Analyzing the else part, the general meaning should be quite obvious, which is to store the elements in each node in the linked list in entries.

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5bGx5pyq5a2k44GG,size_20,color_FFFFFF,t_70,g_se,x_16

According to logical analysis, the following is also easy to understand, which is to string all the strings into a whole string, including brackets, commas and other symbols. In fact, this is the end of the analysis. I won't deduct the minutiae, and they are all relatively basic things, including the handling of errors and so on.

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5bGx5pyq5a2k44GG,size_20,color_FFFFFF,t_70,g_se,x_16

This function can be used to print an array

3. cJSON instance

3.1, test.c test code in the source package

Some running results:

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5bGx5pyq5a2k44GG,size_20,color_FFFFFF,t_70,g_se,x_16

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5bGx5pyq5a2k44GG,size_20,color_FFFFFF,t_70,g_se,x_16

3.2. Use cases

Example:

{

    "name": "Andy", //key-value pair 1

    "age": 20 //key-value pair 2

}

Code display:

void Parse_Str1(void){    char str1[] = "{"name":"Andy","age":20}";    cJSON *str1_json, *str1_name, *str1_age;    printf("str1:%s

",str1);    str1_json = cJSON_Parse(str1);   //创建JSON解析对象,返回JSON格式是否正确    if (!str1_json)    {        printf("JSON格式错误:%s

", cJSON_GetErrorPtr()); //输出json格式错误信息    }    else    {        printf("JSON格式正确:
%s

",cJSON_Print(str1_json) );        str1_name = cJSON_GetObjectItem(str1_json, "name"); //获取name键对应的值的信息        if (str1_name->type == cJSON_String)        {            printf("姓名:%s
", str1_name->valuestring);        }        str1_age = cJSON_GetObjectItem(str1_json, "age");   //获取age键对应的值的信息        if(str1_age->type==cJSON_Number)        {            printf("年龄:%d
", str1_age->valueint);        }        cJSON_Delete(str1_json);//释放内存    }}

operation result:

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5bGx5pyq5a2k44GG,size_20,color_FFFFFF,t_70,g_se,x_16

Reference: https://blog.csdn.net/qq_38289815/article/details/103307262

Guess you like

Origin blog.csdn.net/HouGOD/article/details/124038350