cJSON学习笔记

安装

https://github.com/DaveGamble/cJSON

组包

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

int main(){
    // 创建dict结点
    cJSON *root = cJSON_CreateObject();

    // 添加string子节点
    cJSON_AddItemToObject(root, "name", cJSON_CreateString("Jack (\"Bee\") Nimble"));

    // 添加number子节点
    cJSON_AddItemToObject(root, "age", cJSON_CreateNumber(20));

    // 添加bool子节点
    cJSON_AddItemToObject(root, "best", cJSON_CreateBool(cJSON_True));

    // 添加bool子节点
    cJSON_AddItemToObject(root, "worse", cJSON_CreateFalse());

    // 添加NULL子节点
    cJSON_AddItemToObject(root, "address", cJSON_CreateNull());

    // 添加array
    cJSON *string_array = cJSON_CreateArray();
    cJSON_AddItemToObject(string_array,"hello",cJSON_CreateString("hello"));
    cJSON_AddItemToObject(string_array,"world",cJSON_CreateString("world"));
    cJSON_AddItemToObject(root,"string",string_array);

    // 添加array
    int nums[] = {1,2,3,4,5,6};
    cJSON *nums_array = cJSON_CreateIntArray(nums,sizeof(nums)/sizeof(int));
    cJSON_AddItemToObject(root,"nums",nums_array);

    // 格式化输出, 调用malloc
    printf("%s\n",cJSON_Print(root));

    // 非格式化输出, 调用malloc
    printf("%s\n",cJSON_PrintUnformatted(root));

    // 不调用malloc
    char buffer[4096] = {0};
    cJSON_PrintPreallocated(root,buffer,sizeof(buffer),0);//0 非格式化, 1格式化
    printf("%s\n",buffer);

    // 删除节点, 如果直接调用cJSON_Delete删除某个节点, print的时候会出错的
    cJSON_DeleteItemFromObjectCaseSensitive(root,"age");// 大小写敏感
    // cJSON_DeleteItemFromObject// 大小写不敏感

    // 更新节点
    cJSON_ReplaceItemInObjectCaseSensitive(root,"name",cJSON_CreateString("小明"));// 大小写敏感
    // cJSON_ReplaceItemInObject// 大小写不敏感

    // 格式化输出, 调用malloc
    printf("%s\n",cJSON_Print(root));

    // 删除根节点
    cJSON_Delete(root);
}

解包

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

int main(){
    char s[] = "{\"name\":\"Jack (\\\"Bee\\\") Nimble\",\"age\":20,\"best\":true,\"worse\":false,\"address\":null,\"string\":[\"hello\",\"world\"],\"nums\":[1,2,3,4,5,6]}";

    cJSON *root, *object, *sub_object;
    
    // 解析
    root = cJSON_Parse(s);

    // 字符串
    object = cJSON_GetObjectItemCaseSensitive(root,"name");
    printf("%d %s\n",cJSON_IsString(object), cJSON_GetStringValue(object));
    object = cJSON_GetObjectItem(root,"NAME");
    printf("%d %s\n",cJSON_IsString(object), cJSON_GetStringValue(object));
    

    // 数字
    object = cJSON_GetObjectItemCaseSensitive(root,"age");
    printf("%d %lf\n",cJSON_IsNumber(object), cJSON_GetNumberValue(object));

    // 数组
    object = cJSON_GetObjectItemCaseSensitive(root,"string");
    printf("%d %d\n",cJSON_IsArray(object), cJSON_GetArraySize(object));
    int i,n = cJSON_GetArraySize(object);
    for(i=0;i<n;i++){
        sub_object = cJSON_GetArrayItem(object,i);
        printf("%d %s\n",cJSON_IsString(sub_object), cJSON_GetStringValue(sub_object));
    }

    // 删除根节点
    cJSON_Delete(root);
}

解包与可变参数

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

#define get_cjson_string(args...) _get_cjson_string(args,"")
int _get_cjson_string(cJSON * root, char * value, int value_size, ...)
{
    va_list ap;
    char *key;
    cJSON *object;

    va_start(ap, value_size);
    for(key=va_arg(ap, char*),object=root;key[0]!='\0';key=va_arg(ap, char*)){
        object = cJSON_GetObjectItemCaseSensitive(object,key);
        if(object == NULL){
            printf("no find %s\n",key);
            break;
        }
    }
    
    va_end(ap);

    if(object==NULL || !cJSON_IsString(object)){
        return -1;
    }

    int len = strlen(cJSON_GetStringValue(object));
    if(len > value_size){
        return -1;
    }

    strcpy(value,cJSON_GetStringValue(object));
    return len;
}

#define get_cjson_number(args...) _get_cjson_number(args,"")
int _get_cjson_number(cJSON * root, double * value, ...){
    va_list ap;
    char *key;
    cJSON *object;

    va_start(ap, value);
    for(key=va_arg(ap, char*),object=root;key[0]!='\0';key=va_arg(ap, char*)){
        object = cJSON_GetObjectItemCaseSensitive(object,key);
        if(object == NULL){
            printf("no find %s\n",key);
            break;
        }
    }
    
    va_end(ap);

    if(object==NULL || !cJSON_IsNumber(object)){
        return -1;
    }

    *value = cJSON_GetNumberValue(object);
    return 0;
}

#define get_cjson_object(args...) _get_cjson_object(args,"")
cJSON * _get_cjson_object(cJSON * root, ...){
    va_list ap;
    char *key;
    cJSON *object;

    va_start(ap, root);
    for(key=va_arg(ap, char*),object=root;key[0]!='\0';key=va_arg(ap, char*)){
        object = cJSON_GetObjectItemCaseSensitive(object,key);
        if(object == NULL){
            printf("no find %s\n",key);
            break;
        }
    }
    
    va_end(ap);

    return object;
}

int main(){
    char s[] = "{\"name\":\"Jack (\\\"Bee\\\") Nimble\",\"age\":20,\"best\":true,\"worse\":false,\"address\":null,\"string\":[\"hello\",\"world\"],\"nums\":[1,2,3,4,5,6]}";

    cJSON *root, *object, *sub_object;
    
    // 解析
    root = cJSON_Parse(s);

    // 字符串
    char tmp[1024] = {0};
    get_cjson_string(root,tmp,sizeof(tmp)-1,"name");
    printf("%s\n",tmp);
    

    // 数字
    double f = 0.0;
    get_cjson_number(root,&f,"age");
    printf("%lf\n",f);

    // 删除根节点
    cJSON_Delete(root);
}

优化

重定义malloc和free, 可以优化组包解包速度
因为绝大部分场景,基本都是调用malloc生成新节点,然后在最后delete根节点的时候一起free释放所有节点。所以可以预先在堆中定义一个大buffer, malloc则从buffer中申请

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

int main(int argc,char** argv){
    unsigned char buff[8192],*p = buff;
    void *my_malloc(size_t sz){
        if(sizeof(buff) - (p-buff) < sz){
            return NULL;
        }
        unsigned char *p_tmp = p;
        p += sz;
        return p_tmp;
    }
    void my_free(void* ptr){
        return;
    }

    cJSON_Hooks hooks = {my_malloc,my_free};
    cJSON_InitHooks(&hooks);// 替换原有的malloc和free

    cJSON *root,*fmt;
    root = cJSON_CreateObject();
    cJSON_AddItemToObject(root, "name", cJSON_CreateString("Jack (\"Bee\") Nimble"));
    cJSON_AddItemToObject(root, "format", fmt = cJSON_CreateObject());
    cJSON_AddStringToObject(fmt, "type", "rect");
    cJSON_AddNumberToObject(fmt, "width", 1920);
    cJSON_AddNumberToObject(fmt, "height", 1080);
    cJSON_AddFalseToObject (fmt, "interlace");
    cJSON_AddNumberToObject(fmt, "frame rate", 24);
    printf("%s\n",cJSON_PrintUnformatted(root));
    cJSON_Delete(root);

    cJSON_InitHooks(NULL);// 还原为原有的malloc和free
}

测试程序

测试环境:树莓派4B
test_stack.c

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

int main(int argc,char** argv){
    int n = 10000,i;
    if(argc > 1){
        n = atoi(argv[1]);
    }

    for(i=0;i<n;i++){
        unsigned char buff[8192],*p = buff;
        void *my_malloc(size_t sz){
            if(sizeof(buff) - (p-buff) < sz){
                return NULL;
            }
            unsigned char *p_tmp = p;
            p += sz;
            return p_tmp;
        }
        void my_free(void* ptr){
            return;
        }

        cJSON_Hooks hooks = {my_malloc,my_free};
        cJSON_InitHooks(&hooks);

        cJSON *root,*fmt;
        root = cJSON_CreateObject();
        cJSON_AddItemToObject(root, "name", cJSON_CreateString("Jack (\"Bee\") Nimble"));
        cJSON_AddItemToObject(root, "format", fmt = cJSON_CreateObject());
        cJSON_AddStringToObject(fmt, "type", "rect");
        cJSON_AddNumberToObject(fmt, "width", 1920);
        cJSON_AddNumberToObject(fmt, "height", 1080);
        cJSON_AddFalseToObject (fmt, "interlace");
        cJSON_AddNumberToObject(fmt, "frame rate", 24);
        // printf("%s\n",cJSON_PrintUnformatted(root));
        cJSON_Delete(root);
        cJSON_InitHooks(NULL);
    }
}

test_heap.c

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

int main(int argc,char** argv){
    int n = 10000,i;
    if(argc > 1){
        n = atoi(argv[1]);
    }

    for(i=0;i<n;i++){
        cJSON *root,*fmt;
        root = cJSON_CreateObject();
        cJSON_AddItemToObject(root, "name", cJSON_CreateString("Jack (\"Bee\") Nimble"));
        cJSON_AddItemToObject(root, "format", fmt = cJSON_CreateObject());
        cJSON_AddStringToObject(fmt, "type", "rect");
        cJSON_AddNumberToObject(fmt, "width", 1920);
        cJSON_AddNumberToObject(fmt, "height", 1080);
        cJSON_AddFalseToObject (fmt, "interlace");
        cJSON_AddNumberToObject(fmt, "frame rate", 24);
        // printf("%s\n",cJSON_PrintUnformatted(root));
        cJSON_Delete(root);
    }
    
    
}

Makefile

SRC = $(wildcard *.c)
OBJ = $(patsubst %.c, %.o, $(SRC))

all : test_stack test_heap

test_stack:test_stack.o cJSON.o
    gcc -o $@ $^

test_heap:test_heap.o cJSON.o
    gcc -o $@ $^

$(OBJ):%.o:%.c
    gcc -c -o $@ $< -O3

clean:
    -rm $(OBJ) $(all)

.PHONY:all clean

1000000次循环测试结果

> time ./test_stack 1000000

real    0m0.868s
user    0m0.867s
sys     0m0.000s

> time ./test_heap 1000000

real    0m3.093s
user    0m3.093s
sys     0m0.000s

参考:

https://blog.csdn.net/Mark_md/article/details/108548524

https://stackoverflow.com/questions/79923/what-and-where-are-the-stack-and-heap

https://stackoverflow.com/questions/24057331/is-accessing-data-in-the-heap-faster-than-from-the-stack

https://stackoverflow.com/questions/161053/which-is-faster-stack-allocation-or-heap-allocation

https://stackoverflow.com/questions/2264969/why-is-memory-allocation-on-heap-much-slower-than-on-stack