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序列化工具解读②(数据解析)

时间: 2024-08-07 06:59:49

cJSON序列化工具解读一(结构剖析)的相关文章

cJONS序列化工具解读三(使用案例)

cJSON使用案例 由了解了cJSON的数据结构,接口以及实现之后,那么我们来举例说明其使用. 本例子是一个简单的学生信息表格管理,我们通过键值对的方式向json中增加元素信息. 然后可以格式化输出结果,也能够反向的由字符串输出生成原cJSON对象. int Test_cJSON() { cJSON* pRoot = cJSON_CreateObject(); cJSON* pArray = cJSON_CreateArray(); cJSON_AddItemToObject(pRoot, "s

Java序列化工具对比

1. Java序列化工具技术原理比较 Binary Formats & language-specific ones JavaBuiltIn(java原生).JavaManual(根据成员变量类型,手工写).FstSerliazation.Kryo Binary formats-generic language-unspecific ones Protobuf(Google).Thrift(Facebook). AvroGeneric.Hessian JSON Format Jackson.Gs

$.ajax、$.post、from表单序列化工具

$.ajax\$.post <script type="text/javascript" language="javascript" src="js/jquery-1.8.3.js" ></script> <script type="text/javascript"> //使用Ajax($.ajax)调用 function ajax(){ $.ajax({ type:"post&q

用序列化工具写入xml

标本: <?xml version="1.0" encoding="UTF-8" standalone="true"?> //文档的申明 <persons> //标签tag <person id=“18"> //ID是person标签的一个属性 <name>allen</name> <age>36</age> </person> <pe

API文档管理工具-数据库表结构思考.

API文档管理工具-数据库表结构思考. PS: 管理工具只是为了方便自己记录API的一些基本信息,方便不同的开发人员 (App Developer, Restful API Developer)之间的工作协调,同时也是由于本人不擅长word文档编写,程序及设计简单,大牛勿喷! API基础信息表 CREATE TABLE API_Infor 2 ( 3 ApiID uniqueidentifier PRIMARY KEY 4 ,ApiCategory int 5 ,ApiSupport nvarc

自定义redis序列化工具

redis一个优点就是可以将数据写入到磁盘中. 我们知道写入磁盘的数据实际上都是以字节(0101这样的二进制数据)的形式写入的. 这意味着如果我们要将一个对象写入磁盘,就必须将这个对象序列化. java的序列化机制可以参考这篇文章. 可以看到java的反序列是否成功跟serialVersionUID有很大的关系,自动生成的UID在每次编译时就会发生变化. 如果有两个程序共享一个redis,这个时候反序列化就会出现问题. 所以总监叫我自定义个redis序列化工具. 一.为什么Spring redi

针对工作中的需要对已有的Java序列化工具分析

针对java原生序列化,以及优化过的java序列化工具列举说明.自己定义的类名(IOSerializable).(Fast Serialization).(FastjsonSerializable) 1.java序列化简介 序列化就是指对象通过写出描述自己状态的数值来记录自己的过程,即将对象表示成一系列有序字节,Java提供了将对象写入流和从流中恢复对象的方法.对象能包含其它的对象,而其它的对象又可以包含另外的对象.JAVA序列化能够自动的处理嵌套的对象.对于一个对象的简单域,writeObje

使用RabbitMQ放置自己定义对象(不借助序列化工具,比如protobuffer)V2.0

怎样使用RabbitMQ盛放自己定义的对象呢?一般都会使用序列化工具在投放之前转换一次.从MQ取回的时候再逆序列化还原为本地对象.这里使用C++自带的强制类型装换,将本地对象的内存模型当做自然的序列化之后的数据,直接当做字节流放到string对象中,从MQ取回的时候用相同的方法再将string对象强制当做字节流转换为本地对象. 直接使用.非常棒 注意:这样的思路事实上有一个缺点就是本地上传,取回本地使用肯定是没问题的.假设本地上传,其它机器使用(本地是大端机器,其它机器是小端机器)就可能有问题,

Google FlatBuffers——开源、跨平台的新一代序列化工具

前段时间刚试用了一个序列化工具cereal,请看cereal:C++实现的开源序列化库,打算再总结下我对google proto buf序列化库的使用呢, 结果还没动手,大Google又出了一个新的.开源.跨平台的序列化工具:FlatBuffers.那就索性先了解了解这个工具把. 一. 什么是Google FlatBuffers FlatBuffers是一个开源的.跨平台的.高效的.提供了C++/Java接口的序列化工具库.它是Google专门为游戏开发或其他性能敏感的应用程序需求而创建.尤其更