引言

  
从前或者是二〇一八年的2018年,写了二个 c json 解析引擎用于3个计算实验数据项目支付中. 尚可. 2018年在网上

看见了许多开源的c json引擎
.对中间一个比较规范的 cJSON 引擎 深入学习了一下.

 从前那个和cJSON比较了一下, 觉得 从前写的不得了 优点是 空间小, 速度快, 因为使用的是 用时解析.而cJSON采用

的是递归下落分析, 结构也正如大.最终 决定再重构贰个用的cjson. 近日设计思路以通用为主.

骨子里 结构 就决定 全数. 等同人的个性.

  

参照资料

1.json 标准     
http://www.json.org/

2.cJSON 源码  
https://sourceforge.net/projects/cjson/

 

吐槽一下

  网上海人民广播广播台湾大学爱人 推荐看cJSON源码, 因为就700多行,能够看看学学. 真的吗 看上面 摘录的代码

1 void   cJSON_ReplaceItemInObject(cJSON *object, const char *string, cJSON *newitem) { int i = 0;cJSON *c = object->child;while (c && cJSON_strcasecmp(c->string, string))i++, c = c->next;if (c) { newitem->string = cJSON_strdup(string);cJSON_ReplaceItemInArray(object, i, newitem); } }

上面正是cJSON中常出现的代码格式,确实 700多行, 这一个700多行全体分手或许要
1400行. 对于这个说700行的心上人只好是呵呵.

json,但有一点,看了许多nb的json引擎,也就 cJSON最简单明白了.最容易学习了. 品质还不错. 这么些是只好说的优点.

背后 再扯一点, 那篇博文也得以清楚为cJSON的深深剖析. 末了小编动用递归下跌分析 语法,构造 cjson_t 结构. 设计比cJSON更好用,更高效.

 

前言

每一趟写博文,发现写的好长,不关心的人很难明白, 本次运用一种新思路. 先将通用的粗略的C开发技术
. 前边再讲主题.

1. 累积的C开发 技巧 看完这几个 你就曾经赚了,
前边就足以不看了

  首先大家分享三个 string convert number 的程序, 首先看上面代码.

// 分析数值的子函数,写的可以
double parse_number(const char* str)
{
    double n = 0.0, ns = 1.0, nd = 0.0; //n把偶才能值, ns表示开始正负, 负为-1, nd 表示小数后面位数
    int e = 0, es = 1; //e表示后面指数, es表示 指数的正负,负为-1
    char c;

    if ((c = *str) == '-' || c == '+') {
        ns = c == '-' ? -1.0 : 1.0; //正负号检测, 1表示负数
        ++str;
    }
    //处理整数部分
    for (c = *str; c >= '0' && c <= '9'; c = *++str)
        n = n * 10 + c - '0';
    if (c == '.')
        for (; (c = *++str) >= '0' && c <= '9'; --nd)
            n = n * 10 + c - '0';

    // 处理科学计数法
    if (c == 'e' || c == 'E') {
        if ((c = *++str) == '+') //处理指数部分
            ++str;
        else if (c == '-')
            es = -1, ++str;
        for (; (c = *str) >= '0' && c <= '9'; ++str)
            e = e * 10 + c - '0';
    }

    //返回最终结果 number = +/- number.fraction * 10^+/- exponent
    n = ns * n * pow(10.0, nd + es * e);
    return n;
}

推荐有心的人多写一回,  支持 +19.09 -19.0
19.567e123 -19.09E-9 这几个转换. 那是项目工程代码,久经考验.

面试也常考. 没啥意思
, 这还有1个 不错的技术 如下

/*
 * 10.1 这里是一个 在 DEBUG 模式下的测试宏 
 *    
 * 用法 :
 * DEBUG_CODE({
 *        puts("debug start...");    
 * });
 */
#ifndef DEBUG_CODE
# ifdef _DEBUG
#    define DEBUG_CODE(code) code
# else
#    define DEBUG_CODE(code) 
# endif // ! _DEBUG
#endif // !DEBUG_CODE

那是个 测试code 宏, 有时候大家想 DEBUG下 测试代码在 Release 情势 删除
. 就用地点宏 .在gcc 下 需求 加上 -I_DEBUG

选取举例如下

/*
* 根据索引得到这个数组中对象
* array    : 数组对象
* idx        : 查找的索引 必须 [0,cjson_getlen(array)) 范围内
*            : 返回查找到的当前对象
*/
cjson_t 
cjson_getarray(cjson_t array, int idx)
{
    cjson_t c;
    DEBUG_CODE({
        if (!array || idx < 0) {
            SL_FATAL("array:%p, idx=%d params is error!", array, idx);
            return NULL;
        }
    });

    for (c = array->child; c&&idx > 0; c = c->next)
        --idx;

    return c;
}

是否很酷. 到那边 能够认为 值了学到了.  前边不佳懂,可看可不看了!

 

正文

1. json 的语法解析 分析

  恭喜到此处了,下边第一个享受的函数还有一种好思路 是 整数某个 和 小数部分分离算,前面再加起来. 就到此地吧.

json 的语法解析 同

json 1

从 value 解析开端 遭遇的 string number true false null 直接解析

到 array
, object 开头解析的时候 解析实现直接 到value 解析, 这样递归下落解析完毕 函数调用的流程图如下:

json 2

譬如说value 解析如下

// 将value 转换塞入 item json值中一部分
static const char* __parse_value(cjson_t item, const char* value)
{
    char c; 
    if ((value) && (c = *value)) {
        switch (c) {
            // n = null, f = false, t = true
        case 'n' : return item->type = _CJSON_NULL, value + 4;
        case 'f' : return item->type = _CJSON_FALSE, value + 5;
        case 't' : return item->type = _CJSON_TRUE, item->vd = 1.0, value + 4;
        case '\"': return __parse_string(item, value);
        case '0' : case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9':
        case '+' : case '-': return __parse_number(item, value);
        case '[' : return __parse_array(item, value);
        case '{' : return __parse_object(item, value);
        }
    }
    // 循环到这里是意外 数据
    SL_WARNING("params value = %s!", value);
    return NULL;
}

 这里是 value函数入口, 再以 array处理为例 流程 如下

// 分析数组的子函数, 采用递归下降分析
static const char* __parse_array(cjson_t item, const char* str)
{
    cjson_t child;
    if (*str != '[') {
        SL_WARNING("array str error start: %s.", str);
        return NULL;
    }

    item->type = _CJSON_ARRAY;
    str = __skip(str + 1);
    if (*str == ']') // 低估提前结束
        return str + 1;

    item->child = child = __cjson_new();
    str = __skip(__parse_value(child, str));
    if (!str) {//解析失败 直接返回
        SL_WARNING("array str error e n d one: %s.", str);
        return NULL;
    }
    while (*str == ',') {
        cjson_t nitem = __cjson_new();
        child->next = nitem;
        nitem->prev = child;
        child = nitem;
        str = __skip(__parse_value(child, __skip(str + 1)));
        if (!str) {// 写代码是一件很爽的事
            SL_WARNING("array str error e n d two: %s.", str);
            return NULL;
        }
    }

    if (*str != ']') {
        SL_WARNING("array str error e n d: %s.", str);
        return NULL;
    }
    return str + 1; // 跳过']'
}

关键看 while中剧情,挨个分析 数组中剧情,最终又导向于 value中.

约莫流程就像上了,通过直接递归处理了 json语法的语句.

 

2.cjson 的 结构解析

第壹看 结构文件

// json 中几种数据类型定义
#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_ISREF    (256)        //set 时候用如果是引用就不释放了
#define _CJSON_ISCONST    (512)        //set时候用, 如果是const char* 就不释放了

struct cjson {
    struct cjson *next, *prev;
    struct cjson *child; // type == _CJSON_ARRAY or type == _CJSON_OBJECT 那么 child 就不为空

    int type;
    char *key;    // json内容那块的 key名称     
    char *vs;    // type == _CJSON_STRING, 是一个字符串     
    double vd;  // type == _CJSON_NUMBER, 是一个num值, ((int)c->vd) 转成int 或 bool
};

//定义cjson_t json类型
typedef struct cjson* cjson_t;

来分析一下 struct cjson 中结构字段意思, 个中 next,prev 明白为双向链表, 为了追寻同一层的 对象.例如

[1,2,3,4,[6,5]] 其中 1,2,3,4,
[6,5] 正是同一层, 6,5 是同一层

4->next 后面 的 child 就是
[6,5]每一趟解析到新的 array 或 object 都用child 导向它.

type 表示项目 暗中同意有在那之中 [0,6], 如上
_CJSON_*

key 用于关联对象.

是否很好驾驭

此间提供接口如下

/*
 * 这个宏,协助我们得到 int 值 或 bool 值 
 * 
 * item : 待处理的目标cjson_t结点
 */
#define cjson_getint(item) \
    ((int)((item)->vd))

/*
 *  删除json串内容  
 * c        : 待释放json_t串内容
 */
extern void cjson_delete(cjson_t* pc);

/*
 * 对json字符串解析返回解析后的结果
 * jstr        : 待解析的字符串
 */
extern cjson_t cjson_parse(const char* jstr);

/*
 * 根据 item当前结点的 next 一直寻找到 NULL, 返回个数
 *推荐是数组使用
 * array    : 待处理的cjson_t数组对象
 *            : 返回这个数组中长度
 */
extern int cjson_getlen(cjson_t array);

/*
 * 根据索引得到这个数组中对象
 * array    : 数组对象
 * idx        : 查找的索引 必须 [0,cjson_getlen(array)) 范围内
 *            : 返回查找到的当前对象
 */
extern cjson_t cjson_getarray(cjson_t array, int idx);

/*
 * 根据key得到这个对象 相应位置的值
 * object    : 待处理对象中值
 * key        : 寻找的key
 *            : 返回 查找 cjson_t 对象
 */
extern cjson_t cjson_getobject(cjson_t object, const char* key);

看壹遍是不是就领悟了,看代码照旧比较好懂的. 自个儿写就难一些了. 首要难在

统一筹划 => 开发 => 测试 => 优化
=> 设计 => 开发 => 测试
……………………………. 流程很多,出3个好东西最难的是时刻和执着.

 

3.cjson 部分源码分析

先看最根本的 内存释放代码

// 删除cjson
static void __cjson_delete(cjson_t c)
{
    cjson_t next;
    while (c) {
        next = c->next;
        //递归删除儿子
        if (!(c->type & _CJSON_ISREF)) {
            if (c->child) //如果不是尾递归,那就先递归
                __cjson_delete(c->child);
            if (c->vs)
                free(c->vs);
        }
        else if (!(c->type & _CJSON_ISCONST) && c->key)
            free(c->key);
        free(c);
        c = next;
    }
}

/*
*  删除json串内容,最近老是受清华的老学生打击, 会起来的......
* c        : 待释放json_t串内容
*/
void 
cjson_delete(cjson_t* pc)
{
    if (!pc || !*pc)
        return;
    __cjson_delete(*pc);
    *pc = NULL;
}

下边做法是 防止野指针, 用时间换安全. 时间空间安全 三要素,基本正是编制程序三大成分. 下面
_CJSOn_ISREF 是为了 set 前边设计留的, 添加了不必要释放的事物

我们就不处理.

在看叁个得到涉及对象的值

/*
* 根据key得到这个对象 相应位置的值
* object    : 待处理对象中值
* key        : 寻找的key
*            : 返回 查找 cjson_t 对象
*/
cjson_t 
cjson_getobject(cjson_t object, const char* key)
{
    cjson_t c;
    DEBUG_CODE({
        if (!object || !key || !*key) {
            SL_FATAL("object:%p, key=%s params is error!", object, key);
            return NULL;
        }
    });

    for (c = object->child; c && str_icmp(key, c->key); c = c->next)
        ;

    return c;
}

是还是不是很不难 一下都知道了. 个中 str_icmp 上一篇博文中近乎讲过源码 如下

/*
 * 这是个不区分大小写的比较函数
 * ls       : 左边比较字符串
 * rs       : 右边比较字符串
 *          : 返回 ls>rs => >0 ; ls = rs => 0 ; ls<rs => <0
 */
int 
str_icmp(const char* ls, const char* rs)
{
    int l, r;
    if(!ls || !rs)
        return (int)ls - (int)rs;

    do {
        if((l=*ls++)>='a' && l<='z')
            l -= 'a' - 'A';
        if((r=*rs++)>='a' && r<='z')
            r -= 'a' - 'A';
    } while(l && l==r);

    return l-r;
}

到此地 近年来 了然的宏图基本就竣事了.

 

4.cjson 源码源码呈现

此地正是平时呈现全部的源码 首先是 cjson.h

#ifndef _H_CJSON
#define _H_CJSON

// json 中几种数据类型定义
#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_ISREF    (256)        //set 时候用如果是引用就不释放了
#define _CJSON_ISCONST    (512)        //set时候用, 如果是const char* 就不释放了

struct cjson {
    struct cjson *next, *prev;
    struct cjson *child; // type == _CJSON_ARRAY or type == _CJSON_OBJECT 那么 child 就不为空

    int type;
    char *key;    // json内容那块的 key名称     
    char *vs;    // type == _CJSON_STRING, 是一个字符串     
    double vd;  // type == _CJSON_NUMBER, 是一个num值, ((int)c->vd) 转成int 或 bool
};

//定义cjson_t json类型
typedef struct cjson* cjson_t;

/*
 * 这个宏,协助我们得到 int 值 或 bool 值 
 * 
 * item : 待处理的目标cjson_t结点
 */
#define cjson_getint(item) \
    ((int)((item)->vd))

/*
 *  删除json串内容  
 * c        : 待释放json_t串内容
 */
extern void cjson_delete(cjson_t* pc);

/*
 * 对json字符串解析返回解析后的结果
 * jstr        : 待解析的字符串
 */
extern cjson_t cjson_parse(const char* jstr);

/*
 * 根据 item当前结点的 next 一直寻找到 NULL, 返回个数
 *推荐是数组使用
 * array    : 待处理的cjson_t数组对象
 *            : 返回这个数组中长度
 */
extern int cjson_getlen(cjson_t array);

/*
 * 根据索引得到这个数组中对象
 * array    : 数组对象
 * idx        : 查找的索引 必须 [0,cjson_getlen(array)) 范围内
 *            : 返回查找到的当前对象
 */
extern cjson_t cjson_getarray(cjson_t array, int idx);

/*
 * 根据key得到这个对象 相应位置的值
 * object    : 待处理对象中值
 * key        : 寻找的key
 *            : 返回 查找 cjson_t 对象
 */
extern cjson_t cjson_getobject(cjson_t object, const char* key);

#endif // !_H_CJSON

后买你是 cjson.c 的兑现

#include <cjson.h>
#include <schead.h>
#include <sclog.h>
#include <tstring.h>
#include <math.h>

// 删除cjson
static void __cjson_delete(cjson_t c)
{
    cjson_t next;
    while (c) {
        next = c->next;
        //递归删除儿子
        if (!(c->type & _CJSON_ISREF)) {
            if (c->child) //如果不是尾递归,那就先递归
                __cjson_delete(c->child);
            if (c->vs)
                free(c->vs);
        }
        else if (!(c->type & _CJSON_ISCONST) && c->key)
            free(c->key);
        free(c);
        c = next;
    }
}

/*
*  删除json串内容,最近老是受清华的老学生打击, 会起来的......
* c        : 待释放json_t串内容
*/
void 
cjson_delete(cjson_t* pc)
{
    if (!pc || !*pc)
        return;
    __cjson_delete(*pc);
    *pc = NULL;
}

//构造一个空 cjson 对象
static inline cjson_t __cjson_new(void)
{
    cjson_t c = calloc(1, sizeof(struct cjson));
    if (!c) {
        SL_FATAL("calloc sizeof struct cjson error!");
        exit(_RT_EM);
    }
    return c;
}

// 简化的代码段,用宏来简化代码书写 , 16进制处理
#define __parse_hex4_code(c, h) \
    if (c >= '0' && c <= '9') \
        h += c - '0'; \
    else if (c >= 'A' && c <= 'F') \
        h += 10 + c - 'A'; \
    else if (c >= 'a' && c <= 'z') \
        h += 10 + c - 'F'; \
    else \
        return 0

// 等到unicode char代码
static unsigned __parse_hex4(const char* str)
{
    unsigned h = 0;
    char c = *str;
    //第一轮
    __parse_hex4_code(c, h);
    h <<= 4;
    c = *++str;
    //第二轮
    __parse_hex4_code(c, h);
    h <<= 4;
    c = *++str;
    //第三轮
    __parse_hex4_code(c, h);
    h <<= 4;
    c = *++str;
    //第四轮
    __parse_hex4_code(c, h);

    return h;
}

// 分析字符串的子函数,
static const char* __parse_string(cjson_t item, const char* str)
{
    static unsigned char __marks[] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC };
    const char *ptr;
    char *nptr, *out;
    int len;
    char c;
    unsigned uc, nuc;

    if (*str != '\"') { // 检查是否是字符串内容
        SL_WARNING("need \\\" str => %s error!", str);
        return NULL;
    }

    for (ptr = str + 1, len = 0; (c = *ptr++) != '\"' && c; ++len)
        if (c == '\\') //跳过转义字符
            ++ptr;
    if (!(out = malloc(len + 1))) {
        SL_FATAL("malloc %d size error!", len + 1);
        return NULL;
    }
    // 这里复制拷贝内容
    for (ptr = str + 1, nptr = out; (c = *ptr) != '\"' && c; ++ptr) {
        if (c != '\\') {
            *nptr++ = c;
            continue;
        }
        // 处理转义字符
        switch ((c = *++ptr)) {
        case 'b': *nptr++ = '\b'; break;
        case 'f': *nptr++ = '\f'; break;
        case 'n': *nptr++ = '\n'; break;
        case 'r': *nptr++ = '\r'; break;
        case 't': *nptr++ = '\t'; break;
        case 'u': // 将utf16 => utf8, 专门的utf处理代码
            uc = __parse_hex4(ptr + 1);
            ptr += 4;//跳过后面四个字符, unicode
            if ((uc >= 0xDC00 && uc <= 0xDFFF) || uc == 0)    break;    /* check for invalid.    */

            if (uc >= 0xD800 && uc <= 0xDBFF)    /* UTF16 surrogate pairs.    */
            {
                if (ptr[1] != '\\' || ptr[2] != 'u')    
                    break;    /* missing second-half of surrogate.    */
                nuc = __parse_hex4(ptr + 3);
                ptr += 6;
                if (nuc < 0xDC00 || nuc>0xDFFF)        
                    break;    /* invalid second-half of surrogate.    */
                uc = 0x10000 + (((uc & 0x3FF) << 10) | (nuc & 0x3FF));
            }

            len = 4;
            if (uc < 0x80) 
                len = 1;
            else if (uc < 0x800) 
                len = 2;
            else if (uc < 0x10000) 
                len = 3; 
            nptr += len;

            switch (len) {
            case 4: *--nptr = ((uc | 0x80) & 0xBF); uc >>= 6;
            case 3: *--nptr = ((uc | 0x80) & 0xBF); uc >>= 6;
            case 2: *--nptr = ((uc | 0x80) & 0xBF); uc >>= 6;
            case 1: *--nptr = (uc | __marks[len]);
            }
            nptr += len;
            break;
        default: *nptr++ = c;
        }
    }

    *nptr = '\0';
    if (c == '\"')
        ++ptr;
    item->vs = out;
    item->type = _CJSON_STRING;
    return ptr;
}

// 分析数值的子函数,写的可以
static const char* __parse_number(cjson_t item, const char* str)
{
    double n = 0.0, ns = 1.0, nd = 0.0; //n把偶才能值, ns表示开始正负, 负为-1, nd 表示小数后面位数
    int e = 0, es = 1; //e表示后面指数, es表示 指数的正负,负为-1
    char c;

    if ((c = *str) == '-' || c == '+') {
        ns = c == '-' ? -1.0 : 1.0; //正负号检测, 1表示负数
        ++str;
    }
    //处理整数部分
    for (c = *str; c >= '0' && c <= '9'; c = *++str)
        n = n * 10 + c - '0';
    if (c == '.')
        for (; (c = *++str) >= '0' && c <= '9'; --nd)
            n = n * 10 + c - '0';

    // 处理科学计数法
    if (c == 'e' || c == 'E') {
        if ((c = *++str) == '+') //处理指数部分
            ++str;
        else if (c == '-')
            es = -1, ++str;
        for (; (c = *str) >= '0' && c <= '9'; ++str)
            e = e * 10 + c - '0';
    }

    //返回最终结果 number = +/- number.fraction * 10^+/- exponent
    n = ns * n * pow(10.0, nd + es * e);
    item->vd = n;
    item->type = _CJSON_NUMBER;
    return str;
}

// 跳过不需要处理的字符
static const char* __skip(const char* in)
{
    if (in && *in && *in <= 32) {
        unsigned char c;
        while ((c = *++in) && c <= 32)
            ;
    }
    return in;
}

// 递归下降分析 需要声明这些函数
static const char* __parse_array(cjson_t item, const char* str);
static const char* __parse_object(cjson_t item, const char* str);
static const char* __parse_value(cjson_t item, const char* value);

// 分析数组的子函数, 采用递归下降分析
static const char* __parse_array(cjson_t item, const char* str)
{
    cjson_t child;
    if (*str != '[') {
        SL_WARNING("array str error start: %s.", str);
        return NULL;
    }

    item->type = _CJSON_ARRAY;
    str = __skip(str + 1);
    if (*str == ']') // 低估提前结束
        return str + 1;

    item->child = child = __cjson_new();
    str = __skip(__parse_value(child, str));
    if (!str) {//解析失败 直接返回
        SL_WARNING("array str error e n d one: %s.", str);
        return NULL;
    }
    while (*str == ',') {
        cjson_t nitem = __cjson_new();
        child->next = nitem;
        nitem->prev = child;
        child = nitem;
        str = __skip(__parse_value(child, __skip(str + 1)));
        if (!str) {// 写代码是一件很爽的事
            SL_WARNING("array str error e n d two: %s.", str);
            return NULL;
        }
    }

    if (*str != ']') {
        SL_WARNING("array str error e n d: %s.", str);
        return NULL;
    }
    return str + 1; // 跳过']'
}

// 分析对象的子函数
static const char* __parse_object(cjson_t item, const char* str)
{
    cjson_t child;
    if (*str != '{') {
        SL_WARNING("object str error start: %s.", str);
        return NULL;
    }

    item->type = _CJSON_OBJECT;
    str = __skip(str + 1);
    if (*str == '}')
        return str + 1;

    //处理结点, 开始读取一个 key
    item->child = child = __cjson_new();
    str = __skip(__parse_string(child, str));
    if (!str || *str != ':') {
        SL_WARNING("__skip __parse_string is error : %s!", str);
        return NULL;
    }
    child->key = child->vs;
    child->vs = NULL;

    str = __skip(__parse_value(child, __skip(str + 1)));
    if (!str) {
        SL_WARNING("__skip __parse_string is error 2!");
        return NULL;
    }

    // 递归解析
    while (*str == ',') {
        cjson_t nitem = __cjson_new();
        child->next = nitem;
        nitem->prev = child;
        child = nitem;
        str = __skip(__parse_string(child, __skip(str + 1)));
        if (!str || *str != ':'){
            SL_WARNING("__parse_string need name or no equal ':' %s.", str);
            return NULL;
        }
        child->key = child->vs;
        child->vs = NULL;

        str = __skip(__parse_value(child, __skip(str+1)));
        if (!str) {
            SL_WARNING("__parse_string need item two ':' %s.", str);
            return NULL;
        }
    }

    if (*str != '}') {
        SL_WARNING("object str error e n d: %s.", str);
        return NULL;
    }
    return str + 1;
}

// 将value 转换塞入 item json值中一部分
static const char* __parse_value(cjson_t item, const char* value)
{
    char c; 
    if ((value) && (c = *value)) {
        switch (c) {
            // n = null, f = false, t = true
        case 'n' : return item->type = _CJSON_NULL, value + 4;
        case 'f' : return item->type = _CJSON_FALSE, value + 5;
        case 't' : return item->type = _CJSON_TRUE, item->vd = 1.0, value + 4;
        case '\"': return __parse_string(item, value);
        case '0' : case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9':
        case '+' : case '-': return __parse_number(item, value);
        case '[' : return __parse_array(item, value);
        case '{' : return __parse_object(item, value);
        }
    }
    // 循环到这里是意外 数据
    SL_WARNING("params value = %s!", value);
    return NULL;
}

/*
* 对json字符串解析返回解析后的结果
* jstr        : 待解析的字符串
*            : 返回解析好的字符串内容
*/
cjson_t 
cjson_parse(const char* jstr)
{
    cjson_t c = __cjson_new();
    const char* end;

    if (!(end = __parse_value(c, __skip(jstr)))) {
        SL_WARNING("__parse_value params end = %s!", end);
        cjson_delete(&c);
        return NULL;
    }

    //这里是否检测 返回测试数据
    return c;
}

/*
* 根据 item当前结点的 next 一直寻找到 NULL, 返回个数
*推荐是数组使用
* array    : 待处理的cjson_t数组对象
*            : 返回这个数组中长度
*/
int 
cjson_getlen(cjson_t array)
{
    int len = 0;
    if (array)
        for (array = array->child; array; array = array->next)
            ++len;

    return len;
}

/*
* 根据索引得到这个数组中对象
* array    : 数组对象
* idx        : 查找的索引 必须 [0,cjson_getlen(array)) 范围内
*            : 返回查找到的当前对象
*/
cjson_t 
cjson_getarray(cjson_t array, int idx)
{
    cjson_t c;
    DEBUG_CODE({
        if (!array || idx < 0) {
            SL_FATAL("array:%p, idx=%d params is error!", array, idx);
            return NULL;
        }
    });

    for (c = array->child; c&&idx > 0; c = c->next)
        --idx;

    return c;
}

/*
* 根据key得到这个对象 相应位置的值
* object    : 待处理对象中值
* key        : 寻找的key
*            : 返回 查找 cjson_t 对象
*/
cjson_t 
cjson_getobject(cjson_t object, const char* key)
{
    cjson_t c;
    DEBUG_CODE({
        if (!object || !key || !*key) {
            SL_FATAL("object:%p, key=%s params is error!", object, key);
            return NULL;
        }
    });

    for (c = object->child; c && str_icmp(key, c->key); c = c->next)
        ;

    return c;
}

末端将提交测试代码

 

5.cjson 测试代码 突显

测试的  test_cjson.c

#include <schead.h>
#include <sclog.h>
#include <cjson.h>

// 测试 cjson 函数
int main(int argc, char* argv[])
{
    //注册等待函数
    INIT_PAUSE();

    //启动日志记录功能
    sl_start();

    // 第二个 测试 json 串的解析
    puts("测试 cjson 是否可用");
    char text1[] = "{\n\"name\": \"Jack (\\\"Bee\\\") Nimble\", \n\"format\": {\"type\":       \"rect\", \n\"width\":      1920, \n\"height\":     1080, \n\"interlace\":  false,\"frame rate\": 24\n}\n}";

    cjson_t js = cjson_parse(text1);

    cjson_t name = cjson_getobject(js, "name");
    printf("name => %s\n", name->vs);

    cjson_t format = cjson_getobject(js, "format");
    printf("len(format) => %d\n", cjson_getlen(format));

    cjson_t interlace = cjson_getobject(format, "interlace");
    printf("interlace => %d\n", cjson_getint(interlace));

    cjson_delete(&js);

    //进行第三组测试

    puts(" 测试 数组的读取");
    char text2[] = "[\"Sunday\", \"Monday\", \"Tuesday\", \"Wednesday\", \"Thursday\", \"Friday\", \"Saturday\"]";
    js = cjson_parse(text2);
    int len = cjson_getlen(js);
    int i;
    for (i = 0; i < len; ++i) {
        cjson_t item = cjson_getarray(js,i);
        printf("%d => %s.\n", i, item->vs);
    }
    cjson_delete(&js);


    puts("第三组测试");
    char text3[] = "[\n    [0, -1, 0],\n    [1, 0, 0],\n    [0, 0, 1]\n    ]\n";
    js = cjson_parse(text3);
    len = cjson_getlen(js);
    for (i = 0; i < len; ++i) {
        cjson_t item = cjson_getarray(js, i);
        printf("%d => %d.\n", i, cjson_getlen(item));
    }

    cjson_delete(&js);

    return 0;
}

运营的结果如下

json 3

代码一定是跨平台的, 选取window 是为了 升高调试功用选用VS的DEBUG
. 在Linux 上便是重新 的调节和测试 , 手累. 吐槽一下 在 Linux 上工作久了,

率真手累. 到此处 cjson 解析部分在工程中生产的代码我们都搭建起来. 勉强能够,欢迎尝试

 

后记

  错误是难免的, 欢迎吐槽, 下次分析完整的 cjson 使用进程,补充上cjson的 填充构造进程. 欢迎用在本身的品种中. 只怕学习一下. 

不寻常再相互交换.

 

相关文章

网站地图xml地图