cJSON源码学习总结

JSON介绍

JSON(JavaScript Object Notation) 是一种轻量级的数据交换格式,可以把 JSON 的结构理解成无序的、可嵌套的 key-value 键值对集合,这些 key-value 键值对以结构体或数组的形式来组织的。同一级的 key-value 键值对之间用一个,(逗号) 隔开,每个 key-value 键值对是由一个 key 后面紧接一个:(冒号),冒号后面是这个 key 对应的 value。key 是一个 word,由大小写字母、下划线及数字组成,可以由双引号封闭,也可以不用双引号。而 value 的取值集为: number、boolean(true、false)、null、string、object 和 array。而cJSON则是一个用C 语言写JSON 解析库,特点就是一个简洁。

简单了解cJSON的功能

创建JSON数据

void create_json(const char *filename)//参数为输出文件名
{
    printf("----------------create json start-----------------------------\n");
    //组JSON
    cJSON *root_json = cJSON_CreateObject();
    cJSON *data_json = cJSON_CreateObject();
    cJSON_AddItemToObject(root_json, "data", data_json);//基本的条目添加方法

    //  原子条目创建&添加
    //  cJSON_AddItemToObject(data_json, "Null", cJSON_CreateNull());
    //  cJSON_AddItemToObject(data_json, "True", cJSON_CreateTrue());
    //  cJSON_AddItemToObject(data_json, "False", cJSON_CreateFalse());
    //  cJSON_AddItemToObject(data_json, "bool", cJSON_CreateBool(-1));//不等于0,true
    //  cJSON_AddItemToObject(data_json, "double", cJSON_CreateNumber(1.1));
    //  cJSON_AddItemToObject(data_json, "string", cJSON_CreateString("strings"));
    //  一些直接的添加条目的方法,其实是通过宏添加对cJSON_AddItemToObject的参数实现
    cJSON_AddNullToObject(data_json, "Null");
    cJSON_AddTrueToObject(data_json, "True");
    cJSON_AddFalseToObject(data_json, "False");
    cJSON_AddBoolToObject(data_json, "bool", 0); //等于0,false
    cJSON_AddNumberToObject(data_json, "double", 2.2);
    cJSON_AddStringToObject(data_json, "string", "strings");

    //  数组条目创建&添加
    cJSON *total_array = cJSON_CreateArray();
    cJSON_AddItemToObject(root_json, "array", total_array);//基本的条目添加方法
    int i[] = {1, 2, 3};
    float f[] = {1.2, 2.2, 3.3};
    double d[] = {1.2, 2.2, 3.3};
    char str1[] = "123";
    char str2[] = "234";
    char str3[] = "345";
    const char *str[] = {str1, str2, str3};
    cJSON_AddItemToArray(total_array, cJSON_CreateIntArray(i, 3));
    cJSON_AddItemToArray(total_array, cJSON_CreateFloatArray(f, 3));
    cJSON_ReplaceItemInArray(total_array, 2, cJSON_CreateDoubleArray(d, 3)); //替换2(第三个)
    cJSON_InsertItemInArray(total_array, 0, cJSON_CreateStringArray(str, 3)); //插到第一

    cJSON_AddItemToObject(root_json, "Replace", cJSON_CreateObject());
    //  替换条目内容
    cJSON_ReplaceItemInObject(root_json, "Replace", cJSON_CreateNull());

    //分离和删除,略
    //cJSON *DetachItem_object=cJSON_DetachItemFromObject(cJSON *object,const char *string);
    //cJSON_DeleteItemFromObject(cJSON *object,const char *string);
    //cJSON *DetachItem_arraycJSON_DetachItemFromArray(cJSON *array,int which);
    //cJSON_DeleteItemFromArray(cJSON *array,int which);
    //引用性添加节点,略
    // cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item);
    // cJSON_AddItemReferenceToObject(cJSON *object,const char *string,cJSON *item);
    //复制条目,略
    //cJSON *cJSON_Duplicate(cJSON *item,int recurse);

    //打印JSON
    char *out = cJSON_Print(root_json);
    printf("%s\n", out);

    FILE *fp = fopen(filename, "wb"); //写入文件
    fprintf(fp, out);
    fclose(fp);

    free(out);
    cJSON_Delete(root_json);
    printf("----------------create json end-------------------------------\n");
}

解析JSON数据

void parse_json(const char *filename)//参数为输入文件名
{
    printf("----------------parse json start-------------------------------\n");

    //从文件中读取要解析的JSON数据
    FILE *fp = fopen(filename, "rb");
    fseek(fp, 0, SEEK_END);
    long len = ftell(fp);
    fseek(fp, 0, SEEK_SET);
    char *data = (char *)malloc(len + 1);
    fread(data, 1, len, fp);
    fclose(fp);
    data[len] = '\0'; //修改最后的EOF为\0
    printf("%s", data);
    printf("\n");

    //解析JSON数据
    cJSON *root_json = cJSON_Parse(data);    //将字符串解析成json结构体
    if (NULL == root_json)
    {
        printf("error:%s\n", cJSON_GetErrorPtr());
        cJSON_Delete(root_json);
        return;
    }

    //解析一个条目
    cJSON *one_item = cJSON_GetObjectItem(root_json, "data");
    if (one_item  != NULL)
    {
        char *str = cJSON_Print(one_item );    //将JSON结构体打印到字符串中需要自己释放
        printf("%s: %s\n", one_item ->string, str);
        free(str);
    }

    //解析数组相关,略
    //  cJSON *cJSON_GetArrayItem(cJSON *array,int item);
    //  cJSON_GetArraySize(cJSON *array);

    //一层遍历解析,输出显示
    cJSON *item = cJSON_GetObjectItem(root_json, "name");
    cJSON_ArrayForEach(item, root_json)
    {
        printf("%s: %s\n", item->string, cJSON_Print(item));
    }

    free(item);
    free(data);
    cJSON_Delete(root_json);
    printf("----------------parse json end--------------------------------\n");
}

源码摘抄分析

定义的内部基本数据类型:

只是通过简单的宏定义实现。

#define cJSON_False 0
#define cJSON_True 1
#define cJSON_NULL 2
#define cJSON_Number 3
#define cJSON_String 4
#define cJSON_Array 5
#define cJSON_Object 6

#define cJSON_IsReference 256
#define cJSON_StringIsConst 512

所使用的基本数据结构:

理解了这个数据结构就基本可以了解实现原理了。

/*
typedef struct cJSON {
 struct cJSON *next,*prev;
 struct cJSON *child;
 int type;
 char *valuestring;
 int valueint;
 double valuedouble;
 char *string;
} cJSON;
1.cJOSN 结构体为一个双向列表,并可通过 child 指针访问下一层。
2.type 变量决定数据项类型(键的类型),
  数据项可以是字符串可以是整形,也可以是浮点型。
  如果是整形值的话可从 valueint,如果是浮点型的话可从 valuedouble 取出,以此类推。
3.string 可理解为节点的名称,综合此处的第2点可理解为child “键”的名称。
*/

具体操作实现

首先提一下,内存操作可以替换为其他自定义的 malloc 和 free 函数

static void *(*cJSON_malloc)(size_t sz) = malloc;
static void (*cJSON_free)(void *ptr) = free;

所有对JSON中条目的操作实际都是在对一条双向链表各个节点(还有子节点)的操作,

创建节点

static cJSON *cJSON_New_Item(void)
{
    cJSON *node = (cJSON *)cJSON_malloc(sizeof(cJSON));
    if (node) memset(node, 0, sizeof(cJSON));
    return node;
}

添加子节点

void   cJSON_AddItemToObjectCS(cJSON *object, const char *string, cJSON *item)
{
    if (!item) return;
    if (!(item->type & cJSON_StringIsConst) && item->string) cJSON_free(item->string);
    item->string = (char *)string;
    item->type |= cJSON_StringIsConst;
    cJSON_AddItemToArray(object, item);
}

创建引用节点

static cJSON *create_reference(cJSON *item)
{
    cJSON *ref = cJSON_New_Item();
    if (!ref) return 0;
    memcpy(ref, item, sizeof(cJSON));
    ref->string = 0;
    ref->type |= cJSON_IsReference;
    ref->next = ref->prev = 0;
    return ref;
}

删除节点

void cJSON_Delete(cJSON *c)
{
    cJSON *next;
    while (c)
    {
        next = c->next;
        if (!(c->type & cJSON_IsReference) && c->child) cJSON_Delete(c->child);
        if (!(c->type & cJSON_IsReference) && c->valuestring) cJSON_free(c->valuestring);
        if (!(c->type & cJSON_StringIsConst) && c->string) cJSON_free(c->string);
        cJSON_free(c);
        c = next;
    }
}

其他

限于篇幅,还有删除子节点,复制节点,修改节点内容,替换节点内容,获取对应节点内容等等,都容易看明白。
接下来一大块就是解析字符串生成JSON数据的实现,还有相对的格式化输出。这部分有很多可以学习的东西。

总结

确实对于JSON有了更深的了解,也对链表操作多一点熟悉感,
不过主要还是对一个开源项目有了最基本的印象,看到了各种为了方便他人使用的而进行的设置,易于理解的函数名,实用的宏设置等等。
小而美的实用项目,希望今后自己也可以写出一个像这样简洁实用的作品。

你可能感兴趣的:(其他)