cjson源代码解读 (二)解析流程

先从test.c 开始说, 我们看到test.c 里面有两个函数用来测试, doit 和dofile,  一个是读char* 一个是读file, 肯定是读字符串的要简单, 所以先看doit.

/* Parse text to JSON, then render back to text, and print! */
void doit(char *text)
{
    char *out;cJSON *json;

    json=cJSON_Parse(text);
    if (!json) {printf("Error before: [%s]\n",cJSON_GetErrorPtr());}
    else
    {
        out=cJSON_Print(json);
        cJSON_Delete(json);
        printf("%s\n",out);
        free(out);
    }
}

先是申请了一个cJSON型的指针, 这个指针应该就是主要的存储结构了. 那这个指针是什么构成的呢?

typedef struct cJSON {
    struct cJSON *next,*prev;    /* next/prev allow you to walk array/object chains. Alternatively, use GetArraySize/GetArrayItem/GetObjectItem */
    struct cJSON *child;        /* An array or object item will have a child pointer pointing to a chain of the items in the array/object. */

    int type;                    /* The type of the item, as above. */

    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;                /* The item‘s name string, if this item is the child of, or is in the list of subitems of an object. */
} cJSON;

1) next和prev是两个指向自己下一个和前一个的指针,  如果next==0 表明是末尾, prev==0表明是开头.

2) string是自己的值, 也就是 {"name":"v1"} 中的name

3) type是当前节点value的类型, 也就是json默认定义的几种.

4) 根据type的种类, 分别有对应的值 valuestring, valueint, valuedouble和 child. true和false还有null当然不需要定义, 因为他们本来值就是自己.

type定义在 <cJSON.h> 开头

/* 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

然后就是核心parse的过程了, 也就是函数 json=cJSON_Parse(text);  代码如下:

/* Default options for cJSON_Parse */
cJSON *cJSON_Parse(const char *value) {return cJSON_ParseWithOpts(value,0,0);}

竟然就这么一行, 这不是糊弄我们吗, 那就接着看cJSON_PaarseWithOpts函数.

定义:

/* ParseWithOpts allows you to require (and check) that the JSON is null terminated, and to retrieve the pointer to the final byte parsed. */
extern cJSON *cJSON_ParseWithOpts(const char *value,const char **return_parse_end,int require_null_terminated);

实现:

/* Parse an object - create a new root, and populate. */
cJSON *cJSON_ParseWithOpts(const char *value,const char **return_parse_end,int require_null_terminated)
{
    const char *end=0;
    cJSON *c=cJSON_New_Item();
    ep=0;
    if (!c) return 0;       /* memory fail */

    end=parse_value(c,skip(value));
    if (!end)    {cJSON_Delete(c);return 0;}    /* parse failure. ep is set. */

    /* if we require null-terminated JSON without appended garbage, skip and then check for a null terminator */
    if (require_null_terminated) {end=skip(end);if (*end) {cJSON_Delete(c);ep=end;return 0;}}
    if (return_parse_end) *return_parse_end=end;
    return c;
}

不管后面两个参数, 后面两个参数都是0. (应该在子函数里面有用)

先大致明确一下流程, 然后再看函数怎么实现的(有一些是错误输出, 暂不看):

1. cJSON *c = cJSON_New_Item(); 申请一个cJSON对象出来, 也就相当于new一个.

2. end=parse_value(c,skip(value));  我们注意, 看doit代码的时候只有一句parse, 也就是真正的parse在这个地方(脑补一下).

3. 剩下的都是错误处理.

那么怎么申请对象呢, 看cJSON_New_Item()函数.

/* Internal constructor. */
static cJSON *cJSON_New_Item(void)
{
    cJSON* node = (cJSON*)cJSON_malloc(sizeof(cJSON));
    if (node) memset(node,0,sizeof(cJSON));
    return node;
}static void *(*cJSON_malloc)(size_t sz) = malloc;

申请一个sizeof(cJSON)结构体大小的空间. 然后将内容初始化.

下面看解析函数

/* Utility to jump whitespace and cr/lf */
static const char *skip(const char *in) {while (in && *in && (unsigned char)*in<=32) in++; return in;}

这个非常好理解,  去除最前面的空格等字符.

/* Parser core - when encountering text, process appropriately. */
static const char *parse_value(cJSON *item,const char *value)
{
    if (!value)                        return 0;    /* Fail on null. */
    if (!strncmp(value,"null",4))    { item->type=cJSON_NULL;  return value+4; }
    if (!strncmp(value,"false",5))    { item->type=cJSON_False; return value+5; }
    if (!strncmp(value,"true",4))    { item->type=cJSON_True; item->valueint=1;    return value+4; }
    if (*value==‘\"‘)                { return parse_string(item,value); }
    if (*value==‘-‘ || (*value>=‘0‘ && *value<=‘9‘))    { return parse_number(item,value); }
    if (*value==‘[‘)                { return parse_array(item,value); }
    if (*value==‘{‘)                { return parse_object(item,value); }

    ep=value;return 0;    /* failure. */
}

1. 如果啥都没有, 直接返回了.

2. 然后是只有一个null或false或true的情况.

3. 然后是转义字符.

4. 然后是数字

5. 然后是数组或者字典.

看到这里也能想明白了, value就是指json的单个value, 所以这个函数应该不止会调用一次的. 接下来就是数字, 字符串, 数组, 字典的解析.

这个会在后面讲.

时间: 2024-10-29 10:48:16

cjson源代码解读 (二)解析流程的相关文章

cjson源代码解读(三) 解析字符串、数字、数组、对象

1.  解析数字 static const char *parse_number(cJSON *item,const char *num) { double n=0,sign=1,scale=0;int subscale=0,signsubscale=1; if (*num=='-') sign=-1,num++; /* Has sign? */ if (*num=='0') num++; /* is zero */ if (*num>='1' && *num<='9') do

cjson源代码解读 (一)介绍

cjson是一个非常小巧的c语言写的json解析器, 代码比较短小精悍, c文件不过千行, 同类代码可在网页 json库 中找到.  项目地址如下: cjson地址 json的格式就不介绍了,  下面五个图能很好的解释json的格式. 一个object以{string:value...}的形式构成 也可以由数组构成 值可以是string, number, object, array, true, false等构成 string构造如下 number构造如下 json的介绍可以在 json格式介绍

Android中measure过程、WRAP_CONTENT详解以及 xml布局文件解析流程浅析

转自:http://www.uml.org.cn/mobiledev/201211221.asp 今天,我着重讲解下如下三个内容: measure过程 WRAP_CONTENT.MATCH_PARENT/FILL_PARENT属性的原理说明 xml布局文件解析成View树的流程分析. 希望对大家能有帮助.- - 分析版本基于Android 2.3 . 1.WRAP_CONTENT.MATCH_PARENT/FILL_PARENT 初入Android殿堂的同学们,对这三个属性一定又爱又恨.爱的是使

【转】Android中measure过程、WRAP_CONTENT详解以及xml布局文件解析流程浅析(下)

转载请注明出处:http://blog.csdn.net/qinjuning 上篇文章<<Android中measure过程.WRAP_CONTENT详解以及xml布局文件解析流程浅析(上)>>中,我们 了解了View树的转换过程以及如何设置View的LayoutParams的.本文继续沿着既定轨迹继续未完成的job. 主要知识点如下:                 1.MeasureSpc类说明                 2.measure过程详解(揭秘其细节);   

Apache OFbiz entity engine源代码解读

简单介绍 近期一直在看Apache OFbiz entity engine的源代码.为了能够更透彻得理解,也由于之前没有看人别人写过分析它的文章,所以决定自己来写一篇. 首先,我提出一个问题,假设你有兴趣能够想一下它的答案: JDBC真的给数据訪问提供了足够的抽象,以至于你能够在多个支持jdbc訪问的数据库之间随意切换而全然不须要操心你的数据訪问代码吗? 我以前在微博上有过关于该问题的思考: 事实上这个感慨正是来自于我之前在看的一篇关于jdbc的文章,里面提到了jdbc中的一些设计模式(工厂方法

linux内核奇遇记之md源代码解读之十四raid5非条块内读

转载请注明出处:http://blog.csdn.net/liumangxiong 如果是非条块内读,那么就至少涉及到两个条块的读,这就需要分别从这两个条块内读出数据,然后再凑成整个结果返回给上层.接下来我们将看到如何将一个完整的bio读请求拆分成多个子请求下发到磁盘,从磁盘返回之后再重新组合成请求结果返回给上层的. 4097 logical_sector = bi->bi_sector & ~((sector_t)STRIPE_SECTORS-1); 4098 last_sector =

用户访问网页流程、DNS 解析流程

一.用户访问流程 二.DNS解析流程 DNS( Domain Name System)是"域名系统"的英文缩写,是一种组织成域层次结构的计算机和网络服务命名系统,它用于 TCP/IP 网络,它所提供的服务是用来将主机名和域名转换为 IP 地址的工作.俗话说,DNS 就是将网址转化为对外的 IP 地址. 第一步:浏览器会检查缓存中有没有这个域名对应的解析过的IP地址,如果有,该解析过程将会结束.浏览器缓存域名也是有限制的,包括缓存的时间.大小,可以通过 TTL 属性来设置. 第二步:如果

DVB-subtitle解析流程浅

参考ISO13818-1,EN-300743,从TS流解析开始,结合MS6328的code将DVB Subtitle的解析过程简单梳理了一下. 一.TS  PAT  PMT  PES (参考ISO13818-1) 由TS包中取出PAT表 从搜台开始pat 解析: MW_DVB_SI_PSI_Parser ::_ScanStar()下m_pPatParser->Start(PAT_SCAN_TIMEOUT) 这里mapi_demux_section_filter *m_pSecFilter = m

Vue Router 路由守卫:完整的导航解析流程

完整的导航解析流程 1 导航被触发. 2 在失活的组件里调用离开守卫. 3 调用全局的 beforeEach 守卫. 4 在重用的组件里调用 beforeRouteUpdate 守卫 (2.2+). 5 在路由配置里调用 beforeEnter. 6 解析异步路由组件. 7 在被激活的组件里调用 beforeRouteEnter. 8 调用全局的 beforeResolve 守卫 (2.5+). 9 导航被确认. 10 调用全局的 afterEach 钩子. 11 触发 DOM 更新. 12 用