lept_json学习之parse_object
这一节讲parse_object
数据结构
JSON的Object 是以键值对的方式存储的。
{ key:value}这里我们默认key为JSON字符串。
要表示键值对的集合,数据结构有很多种:
- 动态数组(dynamic array):可扩展容量的数组,如 C++ 的 std::vector。
- 有序动态数组(sorted dynamic array):和动态数组相同,但保证元素已排序,可用二分搜寻查询成员。
- 平衡树(balanced tree):平衡二叉树可有序地遍历成员,如红黑树和 C++ 的 std::map(std::multi_map 支持重复键)。
- 哈希表(hash table):通过哈希函数能实现平均 O(1) 查询,如 C++11 的 std::unordered_map(unordered_multimap 支持重复键)。
设一个对象有 n 个成员,数据结构的容量是 m,n ⩽ m,那么一些常用操作的时间/空间复杂度如下:
动态数组 | 有序动态数组 | 平衡树 | 哈希表 | |
---|---|---|---|---|
有序 | 否 | 是 | 是 | 否 |
自定成员次序 | 可 | 否 | 否 | 否 |
初始化 n 个成员 | O(n) | O(n log n) | O(n log n) | 平均 O(n)、最坏 O(n^2) |
加入成员 | 分摊 O(1) | O(n) | O(log n) | 平均 O(1)、最坏 O(n) |
移除成员 | O(n) | O(n) | O(log n) | 平均 O(1)、最坏 O(n) |
查询成员 | O(n) | O(log n) | O(log n) | 平均 O(1)、最坏 O(n) |
遍历成员 | O(n) | O(n) | O(n) | O(m) |
检测对象相等 | O(n^2) | O(n) | O(n) | 平均 O(n)、最坏 O(n^2) |
空间 | O(m) | O(m) | O(n) | O(m) |
为了简单起见,我们的 leptjson 选择用动态数组的方案。
因为object用的数据结构也是动态数组,就导致了object和array非常的相像,基本唯一的区别就只有键值对了。
typedef struct lept_value lept_value;//前向声明
typedef struct lept_member lept_member;
struct lept_value{
//我们可使用 C 语言的 union 来节省内存:
union{
struct {
lept_member* m; size_t size,capacity; }m_obj;
struct {
lept_value* e; size_t size,capacity; }m_arr;/* array */
struct {
char* s; size_t len; }m_str; /* string */
double m_num; /* number */
}u;
lept_type type;
};
struct lept_member {
char* k; size_t klen; /* member key string, key string length */
lept_value v; /* member value */
};
Parse_object
还记得我之前在string类时提到,string_parse里有一个string_parse_raw是后期object 在parse key值时重构得到的。那么就是在这里了。
{key :value,key:value}
我们在解析object时,左花括号({)确认是object,而后解析空白,解析键值,解析冒号,解析value,有逗号解析逗号,没有逗号解析右花括号,最后解析完毕,把整个object值给copy过去。
在内存分配上的操作和array几乎一样。
static int lept_parse_object(lept_context& c, lept_value& v) {
size_t size;
lept_member m;//创建一个member
int ret;//需要返回的object解析状态flag
EXPECT(c, '{');//确定第一个字符为{右花括号
lept_parse_whitespace(c);//解析空白
if (*c.json == '}') {
//空对象
c.json++;
v.type = LEPT_OBJECT;
v.u.m_obj.m = 0;
v.u.m_obj.size = 0;
return LEPT_PARSE_OK;
}
m.k = NULL;//member key置空
size = 0;//size 置零
for (;;) {
char* str; //创建一个临时字符串
lept_init(m.v);//初始化member value值
/* 1. parse key */
if (*c.json != '"') {
//如果key值不是由"开头,则解析错误
ret = LEPT_PARSE_MISS_KEY;
break;
}
//如果key值解析不成功,退出循环
if ((ret = lept_parse_string_raw(c, str, m.klen)) != LEPT_PARSE_OK)
break;
//解析成功则将解析得到的值copy给member key
memcpy(m.k = (char*)malloc(m