• 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]
    ",cJSON_GetErrorPtr());}
        else
        {
            out=cJSON_Print(json);
            cJSON_Delete(json);
            printf("%s
    ",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, 所以这个函数应该不止会调用一次的. 接下来就是数字, 字符串, 数组, 字典的解析.

    这个会在后面讲.

  • 相关阅读:
    设备上下文相关函数 冷夜
    DirectxDraw加载位图 冷夜
    MMO游戏数值框架概述(偏模拟方向) 冷夜
    C++基本数据类型列表 冷夜
    Ryzom相关的项目简介 冷夜
    使用QueryPerformanceFrequency 计算程序执行时间 冷夜
    C和C++运算符 冷夜
    编译错误记录文档 冷夜
    ANSI编码对比表 冷夜
    mscorsvw.exe是什么 冷夜
  • 原文地址:https://www.cnblogs.com/lavi/p/4289941.html
Copyright © 2020-2023  润新知