5. 数组解析
5. 数组解析
章节目录
5.1 JSON数组结构
先来看看前面实现的数组结构
struct __json_value
{
int type;
union
{
char *string;
double number;
json_object_t object;
json_array_t array;
} value;
};
struct __json_array
{
int size;
struct list_head head;
};
struct __json_element
{
struct list_head list;
json_value_t value;
};
可以看出, __json_array
的结构其实很简单, 就是一个链表, head就是头结点, 每个节点里面包含节点的数据域:json_value_t value
和节点的指针域struct list_head list
这个结构里面就包含两个指针.
外部调用:
case '[':
{
ret = __parse_json_array(cursor, end, depth, &val->value.array);
if (ret < 0)
{
return ret;
}
val->type = JSON_VALUE_ARRAY;
break;
}
目标很明确, 实现__parse_json_array
函数.
看看函数声明: static int __parse_json_array(const char *cursor, const char **end, int depth, json_array_t *array)
这里可以看到多了一个参数depth
, 由于array是复合类型, 每一个数组元素都可以为不同的类型, 为了控制数组的深度, 可以通过参数depth
来记录.
static int __parse_json_array(const char *cursor, const char **end, int depth, json_array_t *arr)
{
int ret;
if (depth == JSON_DEPTH_LIMIT)
{
return -3;
}
INIT_LIST_HEAD(&arr->head);
ret = __parse_json_elements(cursor, end, depth + 1, arr);
if (ret < 0)
{
__destroy_json_elements(arr);
return ret;
}
arr->size = ret;
return 0;
}
函数内部先判断了深度, 这里深度判断定义了一个宏,
JSON_DEPTH_LIMIT
为1024, 如此嵌套解析超过1024就报错.然后是使用
list.h
中自带的宏初始化链表结构.核心的就是解析数组的元素, 即
__parse_json_elements
函数.如果出现错误会调用
__destroy_json_elements(arr)
函数, 释放内存.
5.2 解析数组元素
一个JSON数组中可以包含0至多个元素, 且可以嵌套, 每个元素使用逗号,
分隔, 末尾的逗号不合法.
例:
-
合法:
- [](空数组), [1, 2], [1, "hello", true], [1, 2, [3, 4, 5, "hello"], null]
-
不合法:
- [,], [1, 2, ]
现在来分析一下如何解析数组的元素.
解析JSON数组元素的核心就是理解数组的结构和链表的使用.
struct __json_array
{
struct list_head head;
int size;
};
struct __json_element
{
struct list_head list;
json_value_t value;
};
我们分析__json_array
结构, size
顾名思义, 就是显示这个数组的元素个数. head
就是链表的头节点结构. 而__json_element
结构中包含一个数据域json_value_t
和链表结构list
, 可以把__json_element
结构看做是一个普通意义上的链表节点, 包含数据域value
和指针域list
, 而__json_array
结构中的head
就是头指针, 所以我们的解析就是遍历整个字符串, 然后构造一个头指针为**head**
的双向循环链表.
直接上代码:
static int __parse_json_elements(const char *cursor, const char **end, int depth, json_array_t *arr)
{
int ret;
int cnt = 0;
json_element_t *elem;
while (isspace(*cursor))
{
cursor++;
}
if (*cursor == ']')
{
*end = cursor + 1;
return 0;
}
while (1)
{
elem = (json_element_t *)malloc(sizeof(json_element_t));
if (elem == NULL)
{
return -1;
}
ret = __parse_json_value(cursor, &cursor, depth, &elem->value);
if (ret < 0)
{
free(elem);
return ret;
}
list_add_tail(&elem->list, &arr->head);
cnt++;
while (isspace(*cursor))
{
cursor++;
}
if (*cursor == ',')
{
cursor++;
while (isspace(*cursor))
{
cursor++;
}
}
else if (*cursor == ']')
{
break;
}
else
{
return -2;
}
}
*end = cursor + 1;
return cnt;
}
现在看这个代码应该比较简单, 除了正常的跳过空格, 和解析结束判断(判断右中括号), 核心就是26 ~ 34行.
我们知道, JSON数组中的每个元素可以是任何JSON标准中的类型, 在代码中体现就是struct __json_value
.
这里初始化构造了一个元素elem
, 通过__parse_json_value
解析出了一个value
, 也就是获取到了这个元素的数据域, 接下来将这个元素插入以array
结构中的head
为头的链表就可以了.
list_add_tail(&elem->list, &arr->head);
这个语句就实现了这样一个功能, 也就是将elem
插入以arr->head
为头的链表的末尾.
简单说一下list_add_tail
函数, 这个函数是list.h内核链表实现的工具函数, 实现了将指定节点插入链表的末尾的功能.
/**
* list_add_tail - add a new entry
* @new: new entry to be added
* @head: list head to add it before
*
* Insert a new entry before the specified head.
* This is useful for implementing queues.
*/
static inline void list_add_tail(struct list_head *node, struct list_head *head)
{
__list_add(node, head->prev, head);
}
/*
* Insert a new entry between two known consecutive entries.
*
* This is only for internal list manipulation where we know
* the prev/next entries already!
*/
static inline void __list_add(struct list_head *node, struct list_head *prev, struct list_head *next)
{
next->prev = node;
node->next = next;
node->prev = prev;
prev->next = node;
}
可以看到, list_add_tail
函数调用了内部静态函数__list_add
, 只要有基础的链表知识, 就可以看出, __list_add
函数就是将node
插入prev
和next
之间.
那么list_add_tail
为什么是插入链表的末尾呢? 我们知道, list.h的链表实现是一个双向循环链表, 也即第一个元素的前驱元素为末尾元素, 末尾元素的后驱是第一个元素, list_add_tail
调用__list_add
的参数为node
, head->prev
和head
, node
是待插入的元素, 没什么好说的, **head->prev**
就是头元素的前驱, 前面说了头元素的前驱就是末尾元素, 所以, 这里就是将**node**
插入了末尾元素和头元素之间, 也就是成了新的末尾元素.
这里只讲了我自己的理解, 具体的可以通过实际代码学习了解.
至此, 我们就完成了JSON数组的解析, 还有没有提到的__destroy_json_elements
, 实现了json_element_t
的内存释放, 这个后面集中讲.
参考:
[1] Workflow 源码解析 Json parser :part1 parse - 知乎 (zhihu.com)
[2] 深入理解Linux内核之链表 list.h 功能实现原理、接口说明及示例代码_内核链表接口_猪哥-嵌入式的博客-CSDN博客
[3] 一文多图搞懂数据结构的双链表!-腾讯云开发者社区-腾讯云 (tencent.com)