JSON学习笔记

<注>寒假和小伙伴想试着学做项目,主要学习的是腾讯的开源JSON框架RapidJson。然而在学习rapidjson之前首先还要了解JSON是什么,它都有哪些功能等。通过学习“从零开始的 JSON 库教程”后逐渐掌握了解,下为学习记录整理。

学习资料:从零开始的 JSON 库教程:https://github.com/miloyip/json-tutorial

一、JSON相关定义

1.JSON定义:
JSON(JavaScriptObject Notation, JS 对象简谱) 是一种轻量级的数据交换文本格式,采用完全独立于编程语言的文本格式来存储和表示数据。

2.JSON的7种数据结构:
{null,true,false,number,string,array,object}

3.JSON的3个需求:
1)解析(parse):JSON文本解析为树状数据结构;
2)生成(stringify):将数据结构转换成JSON文本;
3)接口(access):提供接口访问数据.
在这里插入图片描述
4.JSON的2个API函数:
1)解析JSON函数
2)访问结果函数

5.JSON的语法子集:
1)JSON-text = ws value ws
//JSON文本由空白(whitespace)值(value)空白(whitespace)组成
2)ws = *(%x20 / %x09 / %x0A / %x0D)
// * 表示可以为零或多个;即ws为零或多个空格符(space U+0020)、制表符(tab U+0009)、换行符(LF U+000A)、回车符(CR U+000D)构成。
3)value = null/true/false

6.JSON的3个错误码(即不符合JSON-text格式)
1)若一个 JSON 只含有空白,传回 LEPT_PARSE_EXPECT_VALUE。
2)若一个值之后,在空白之后还有其他字符,传回 LEPT_PARSE_ROOT_NOT_SINGULAR。
3)若值不是那三种字面值,传回 LEPT_PARSE_INVALID_VALUE。

二、JSON解析
1.lept_parse_value:
利用switch函数,不同的开头case指向7个解析函数

 switch (*c->json) {
        case 't':  return lept_parse_literal(c, v, "true", LEPT_TRUE);
        case 'f':  return lept_parse_literal(c, v, "false", LEPT_FALSE);
        case 'n':  return lept_parse_literal(c, v, "null", LEPT_NULL);
        default:   return lept_parse_number(c, v);
        case '"':  return lept_parse_string(c, v);
        case '[':  return lept_parse_array(c, v);
        case '{':  return lept_parse_object(c, v);
        case '\0': return LEPT_PARSE_EXPECT_VALUE;
    }

2.null/true/false解析(lept_parse_literal):
提取null、true、false进行对比,表述不完全则返回错误。以下为单独解析和整合之后进行解析,可通过阅读代码感受解析过程。
首先以单独解析true为例,了解解析过程----逐一比对。

static int lept_parse_true(lept_context* c, lept_value* v) {
    EXPECT(c, 't');
    if (c->json[0] != 'r' || c->json[1] != 'u' || c->json[2] != 'e')
        return LEPT_PARSE_INVALID_VALUE;
    c->json += 3;
    v->type = LEPT_TRUE;
    return LEPT_PARSE_OK;
}

将true/false/null三个整合,循环进行比对。

static int lept_parse_literal(lept_context* c,
    lept_value* v, const char* literal, lept_type type) 
{
    size_t i;
    EXPECT(c, literal[0]);
    for (i = 0; literal[i + 1]; i++)
        if (c->json[i] != literal[i + 1])
            return LEPT_PARSE_INVALID_VALUE;
    c->json += i;
    v->type = type;
    return LEPT_PARSE_OK;
}

3.number解析(lept_parse_number):
选择以double类型存储数字,所以首先提取double类型数字。由于数字0-9都可以表示数字,所以采用默认default处理。

default:   return lept_parse_number(c, v);

因为数字是无尽的,所以在解析数字时则判断这个数字是否合法,错误则返回该错误类型,无错误解析成功。
Number一般以十进制表示,由负号(正号不表示)、整数、小数、指数组成,指数以科学计数法表示。以下为错误类型分析:
1)整数部分:若以0开头,则只可以为单个0,即0123这种表示不合法;1-9开头后面可跟任意数字
2)小数部分:小数点前后必须有一个或多个数字,只可以有一个小数点。
3)指数部分:E/e前后必须有一个或多个数字,只可以有一个E/e。
4)其他错误:范围错误,其他字符错误(非法字符,+号)
以下为解析数字可能通过的路径图:
解析可能通过路径图

4.String解析(lept_parse_string):
string语法是以一对双引号将字符括起来,即 ”字符 ”,所以遇到 ” 则进入string解析函数。

case '"':  return lept_parse_string(c, v);

若字符串中本身含有双引号,则需要转义符反斜杠\ .如:”a,\”b”.
\”代表着字符串开始和结束,\(转义字符)表示进入转义字符解析

 switch (ch) {
            case '\"':
                *len = c->top - head;
                *str = lept_context_pop(c, *len);
                c->json = p;
                return LEPT_PARSE_OK;
            case '\\':
                switch (*p++) {
                    case '\"': PUTC(c, '\"'); break;
                    case '\\': PUTC(c, '\\'); break;
                    case '/':  PUTC(c, '/' ); break;
                    case 'b':  PUTC(c, '\b'); break;
                    case 'f':  PUTC(c, '\f'); break;
                    case 'n':  PUTC(c, '\n'); break;
                    case 'r':  PUTC(c, '\r'); break;
                    case 't':  PUTC(c, '\t'); break;
                    case 'u':

(略去case u的代码,基本文本解析可省去u这种情况)
JSON支持9种转义序列,其中第九种\u,即Unicode用来表示各种字符。Unicode的格式为:\uXXXX ,而它的转换格式UTF有很多种,其中UTF-8以字节为编码,所以不会存在字节序问题,且ASCII字符只占1字节,故最为流行。
而对于 JSON字符串中的 \uXXXX 是以 16 进制表示码点 U+0000 至 U+FFFF,我们需要以下步骤即可实现解析:
1)解析 4 位十六进制整数为码点;
2)由于字符串是以 UTF-8 存储,我们要把这个码点编码成 UTF-8。
(具体实现略)
我们在解析字符串这整个过程中由于处理的不是单独个体,所以需要借用栈来进行解析。我们要做的是备份栈顶 ---->将解析的字符压栈---->计算长度,一次弹出---->设置至值里
在这里插入图片描述
图1 备份栈顶,开始压栈
在这里插入图片描述
图2 不断压栈至结束
在这里插入图片描述
图3 将字符一次性弹出,分配内存,生成字符串值

5.Array解析(lept_parse_array):
一个数组中可以有一个或多个元素,可以为不同类型,用逗号分隔,所以数组类型属于复合数据类型(可嵌套),像[],[1,2,3],[[1,2],3]均合法。数组的语法为:

*array = %x5B ws [ value ( ws %x2C ws value ) ] ws %x5D
//%x5B 是左中括号 [,%x2C 是逗号 ,,%x5D 是右中括号 ] ,ws 是空白字符。

类似于解析JSON字符串,在解析数组时,因为在开始时不能知道数组的长度,而又需要进行转义,所以需要一个临时缓冲区去存储解析后的结果。我们为此实现了一个动态增长的堆栈,可以不断压入字符,最后一次性把整个字符串弹出,复制至新分配的内存之中。
解析过程类似于解析字符串,相当于是不断地压栈弹出的嵌套操作,将每一次弹出结果压栈,最后整个弹出,分配内存。图解只展示最后结果:
在这里插入图片描述

6.Object解析(lept_parse_object):
对象类似于数组,区别在于对象以花括号包裹,对象由对象成员,即键值对构成。键必须为 JSON 字符串,然后值是任何 JSON 值,中间以冒号 :(U+003A)分隔。对象语法为:

*member = string ws %x3A ws value
object = %x7B ws [ member ( ws %x2C ws member ) ] ws %x7D

解析对象步骤:
1)解析字符串
2)解析冒号
3)解析JSON任意值
4)解析逗号或右花括号
5)释放临时key及栈上的成员
//若以上四步出现错误导致解析失败,直接进行第五步后结束解析。
在这里插入图片描述
三、JSON生成
JSON解析是将JSON文本解析为树形数据结构,而生成器则是与其相反,通过生成器,我们将树形结构转换成单行、无空格的字符串。同时我们需要利用动态变长的堆栈做输出缓冲区来存储生成结果。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

想要暴富的小李

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值