• cJSON序列化工具解读一(结构剖析)


    cJSON简介

    JSON基本信息

    JSON(JavaScript Object Notation)是一种轻量级的数据交换格式。易于人阅读和编写。同时易于机器解析和生成。是一种很好地数据交换语言。

    官方信息解读简介

    JSON构建:基于两种结构

      “名称/值”对 的集合。

      值得有序列表。

    JSON具体结构表示

      对象:一个”名称/值"对的集合  {名称:值,名称:值}

      数组:值得有序集合[值,值]

      值:str,num,true,false,null,object,array。可嵌套

      字符串:由双引号包围的任意数量Unicode字符的集合,反斜杠转义

      数值:类似C,java:没有8进制和具体的编码细节

    cJSON源码解读

    首先给出cJSON文件中头文件函数列表

    我们将根据此进行模块化解读

    ①结构描述

    关于具体的结构图解描述,请参考官方描述

        /* cJSON Types: */
    #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

    以上是具体的类型区分描述,可以改进为使用enum实现更加安全

        /* The cJSON structure: */
        typedef struct cJSON {
            struct cJSON *next, *prev;    /* 如果是同一级别类型元素,使用双项链方式实现 */
            struct cJSON *child;        /* 如果是具体结构或者数组,第一个指针指向内部链 */
    
            int type;                    /*根据以上定义描述所保存对象类型*/
    
            char *valuestring;            /* The item's string, if type==cJSON_String */
            int valueint;                /* The item's number, if type==cJSON_Number */
            double valuedouble;            /* The item's number, if type==cJSON_Number */
    
            char *string;                /* 对于key-value映射结构,表示其key/名称 */
        }cJSON;

    ②关于内存管理

        typedef struct cJSON_Hooks {
            void *(*malloc_fn)(size_t sz);
            void(*free_fn)(void *ptr);
        } cJSON_Hooks;

    注:cJSON的内存管理,提供了用户自主方式的接口。可以通过方法InitHooks来设置自己的内存管理,默认使用malloc,free

    static void *(*cJSON_malloc)(size_t sz) = malloc;
    static void(*cJSON_free)(void *ptr) = free;
    
    void cJSON_InitHooks(cJSON_Hooks* hooks)
    {
        if (!hooks) { /* Reset hooks */
            cJSON_malloc = malloc;
            cJSON_free = free;
            return;
        }
    
        cJSON_malloc = (hooks->malloc_fn) ? hooks->malloc_fn : malloc;
        cJSON_free = (hooks->free_fn) ? hooks->free_fn : free;
    }

    ③结点创建,删除结点

    /* Internal constructor. */
    static cJSON *cJSON_New_Item(void)
    {
        cJSON* node = (cJSON*)cJSON_malloc(sizeof(cJSON));
        if (node) memset(node, 0, sizeof(cJSON));
        return node;
    }
    
    /* Delete a cJSON structure. */

    //删除节点很简单, 先删除儿子,然后清理内存即可。

    
    

    //总结一下就是对于 object 和 array 需要先删除儿子,然后删除自己。
    //对于 字符串, 需要先释放字符串的内存, 再释放自己这块内存。
    //对于其他节点,直接释放自己这块内存。

    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;
        }
    }

    有了结点设置,那么下面就是具体类型结点的实现了:设置类型,设置具体类型的值

        /* These calls create a cJSON item of the appropriate type. */
        extern cJSON *cJSON_CreateNull(void);
        extern cJSON *cJSON_CreateTrue(void);
        extern cJSON *cJSON_CreateFalse(void);
        extern cJSON *cJSON_CreateBool(int b);
        extern cJSON *cJSON_CreateNumber(double num);
        extern cJSON *cJSON_CreateString(const char *string);
        extern cJSON *cJSON_CreateArray(void);
        extern cJSON *cJSON_CreateObject(void);
    
        /* These utilities create an Array of count items. */
        extern cJSON *cJSON_CreateIntArray(const int *numbers, int count);
        extern cJSON *cJSON_CreateFloatArray(const float *numbers, int count);
        extern cJSON *cJSON_CreateDoubleArray(const double *numbers, int count);
        extern cJSON *cJSON_CreateStringArray(const char **strings, int count);
    /* Create basic types: */
    cJSON *cJSON_CreateNull(void) 
    { 
        cJSON *item = cJSON_New_Item();
        if (item)
            item->type = cJSON_NULL;
        return item; 
    }
    
    /* Create Arrays: */
    cJSON *cJSON_CreateIntArray(const int *numbers, int count)
    {
        int i; cJSON *n = 0, *p = 0,
            *a = cJSON_CreateArray();
    
        for (i = 0; a && i < count; i++)
        {
            n = cJSON_CreateNumber(numbers[i]); //申请N个几点
            if (!i)
                a->child = n;//第一个结点链接使用child
            else 
                suffix_object(p, n);//其他结点 :连接prev next
            p = n;
        }return a;
    }

    ④结点操作

        /* Append item to the specified array/object. */
        extern void cJSON_AddItemToArray(cJSON *array, cJSON *item);
        extern void cJSON_AddItemToObject(cJSON *object, const char *string, cJSON *item);
        extern void cJSON_AddItemToObjectCS(cJSON *object, const char *string, cJSON *item);    /* Use this when string is definitely const (i.e. a literal, or as good as), and will definitely survive the cJSON object */
                                                                                                

    /* Append reference to item to the specified array/object. Use this when you want to add an existing cJSON to a new cJSON,
    but don't want to corrupt your existing cJSON.
    */ extern void cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item); extern void cJSON_AddItemReferenceToObject(cJSON *object, const char *string, cJSON *item); /* Remove/Detatch items from Arrays/Objects. */
      //Detatch脱离,使item从arr链中脱离,便于delete
    extern cJSON *cJSON_DetachItemFromArray(cJSON *array, int which); extern void cJSON_DeleteItemFromArray(cJSON *array, int which); extern cJSON *cJSON_DetachItemFromObject(cJSON *object, const char *string); extern void cJSON_DeleteItemFromObject(cJSON *object, const char *string); /* Update array items. */ extern void cJSON_InsertItemInArray(cJSON *array, int which, cJSON *newitem); /* Shifts pre-existing items to the right. */ extern void cJSON_ReplaceItemInArray(cJSON *array, int which, cJSON *newitem); extern void cJSON_ReplaceItemInObject(cJSON *object, const char *string, cJSON *newitem);

    注:①

    Detach 是什么东西呢?
    我们把一个节点从 json 树中删除, 但是不释放内存,而是先保留这个节点的指针, 这样储存在这个节点的信息都保留了下来。
    接下来我们就可以做很多事了, 合适的时候添加到其他对象中, 合适的时候释放内存。
    比如上面的 delete 函数, 就需要真实的删除了, 这个时候我们删除即可。
    而 detach 实现也比较简单, 只是少了一步删除操作。

    //将结点从结构中脱离
    cJSON *cJSON_DetachItemFromArray(cJSON *array, int which) 
    {
        cJSON *c = array->child;
        while (c && which>0)
            c = c->next, which--; 
        if (!c) 
            return 0;
        if (c->prev) 
            c->prev->next = c->next; 
        if (c->next)
            c->next->prev = c->prev;
        if (c == array->child)
            array->child = c->next;
        c->prev = c->next = 0;
        return c;
    }
    void   cJSON_DeleteItemFromArray(cJSON *array, int which) 
    { 
        cJSON_Delete(cJSON_DetachItemFromArray(array, which));
    }

      ②关于以上相关方法,简单的增加结点就是个链表操作了,然后对于

    cJSON_AddItemReferenceToArray这个方法说明
    cJSON除了实现简单的增加结点到结构之外,还有简单考虑效率问题。
    比如同时增加一个结点到两棵树中,那么如果有深浅拷贝问题时,cJSON做法是,增加一个结点为Reference类型,在另一棵树中。这里的做法可以是引用计数,写实拷贝等
    我们来看看cJSON实现吧
    /* Utility for handling references. */
    static cJSON *create_reference(cJSON *item)
    { 
        cJSON *ref = cJSON_New_Item(); 
        if (!ref) 
            return 0; 
        memcpy(ref, item, sizeof(cJSON)); //浅拷贝所有值-->最终保存了结点的value部分
        ref->string = 0; //k-v中的值清空,以便于重新设置
        ref->type |= cJSON_IsReference;  //结点类性是具体item类型的同时也是reference
        ref->next = ref->prev = 0;  //只是当前节点的引用,去除引用的链接
        return ref; 
    }
    
    void    cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item)
    { 
        cJSON_AddItemToArray(array, create_reference(item)); //创建应用结点之后加入目的数组
    }
    void   cJSON_ReplaceItemInArray(cJSON *array, int which, cJSON *newitem)
    {
        cJSON *c = array->child;
        while (c && which>0) 
            c = c->next, which--;   //找到具体位置
        if (!c) 
            return;
        newitem->next = c->next; 
        newitem->prev = c->prev; //连入结构中
        if (newitem->next)
            newitem->next->prev = newitem;//修改后指针
    
        if (c == array->child)  //如果是个孩子,
            array->child = newitem;
        else 
            newitem->prev->next = newitem;  //修改后指针
        c->next = c->prev = 0;//释放被代替的结点 
        cJSON_Delete(c);    
    }

    ⑤查找相关

    /* Get Array size/item / object item. */
    int    cJSON_GetArraySize(cJSON *array) 
    {
        cJSON *c = array->child; 
        int i = 0; 
        while (c)
            i++, c = c->next;
        return i; 
    }
    cJSON *cJSON_GetArrayItem(cJSON *array, int item)
    { 
        cJSON *c = array->child; 
        while (c && item>0) 
            item--, c = c->next; 
        return c; 
    }
    //结构中结点的设置,需要通过变量名来查找
    cJSON *cJSON_GetObjectItem(cJSON *object, const char *string) 
    { 
        cJSON *c = object->child; 
        while (c && cJSON_strcasecmp(c->string, string))
            c = c->next;
        return c; 
    }

    ⑥总结部分:

    通过以上对于cJSON结构的简单学习和剖析,我们不难联想到广义表。其实这个结构的实现就是类似广义表实现:由child递归为广义结构

    关于具体的cJSON数据解析部分,请参考博客《cJONS序列化工具解读②(数据解析)

  • 相关阅读:
    Java equals()和hashCode()重写总结
    常见ORM框架理解
    struts2的第一个小页面
    Java开发StringBuilder类
    “中软综合项目实训”——把学生当员工培养
    中软寻梦
    软件开发学习的5大技巧
    从学生心理入手
    IT技术人员的职业发展方向
    驱动创新 引领未来
  • 原文地址:https://www.cnblogs.com/lang5230/p/5492706.html
Copyright © 2020-2023  润新知