6. 对象解析

6. 对象解析

章节目录

1. 解析总体流程

2. 类型分辨

3. 数值解析

4. 字符串解析

5. 数组解析

6. 对象解析

6.1 JSON对象结构

JSON对象和数组类似, 是一个复合类型, 使用{ }​包括, 数组中每个元素都是一个JSON的value​, 而对象中的每一个成员是一个键值对, 以冒号':'分隔, 且键必须为字符串类型, 值可以为任意JSON类型, 每个成员由逗号分隔.

如: {'memb1":[1, 2, "hello"], "memb2" : "world"}

C语言实现这个结构如下:

typedef struct __json_object json_object_t

struct __json_object
{
    int size;

    struct list_head head;
    struct rb_root root;
};

typedef struct __json_member json_member_t;
struct __json_member
{
    struct list_head list;
    struct rb_node node;
    json_value_t value;
    char key[1];
};

可以看出, json_object_t​的结构和json_array_t​的结构类似, 就是多了一个struct rb_root root​, 这个是linux内核的红黑树实现, 前面说了, 为了提高JSON对象的查找效率, 所以使用了红黑树的结构, 这个不是我们的重点, 所以暂且知道怎么用就行了.

json_element_t​是一个和json_element_t​类似的结构, 内部包含一个value​即数据域表示键值对的值, 一个key​表示键值对的键名, 这里使用char key[1]只是做占位用, 在解析时分配内存. 其他的就是list​和数组元素结构里的list​相同, 然后多了一个红黑树的节点成员, 用于将当前的member​插入以json_object_t​结构中root​为根的红黑树.

外部调用:

    case '{':
    {
        ret = __parse_json_object(cursor, end, depth, &val->value.object);
        if (ret < 0)
        {
            return ret;
        }

        val->type = JSON_VALUE_OBJECT;
        break;
    }

模式和解析其他的类型相似, 看看解析object函数的实现:

static int __parse_json_object(const char *cursor, const char **end, int depth, json_object_t *obj)
{
    int ret;

    if(depth == JSON_DEPTH_LIMIT)
    {
        return -3;
    }

    // 初始化链表和红黑树
    INIT_LIST_HEAD(&obj->head);
    obj->root.rb_node = NULL;

    ret = __parse_json_members(cursor, end, depth + 1, obj);
    if (ret < 0)
    {
        __destroy_json_members(obj);
        return ret;
    }

    obj->size = ret;
    return 0;
}

可以看出, 除了多初始化一个红黑树, 其他的和数组的类似, 其中核心就是__parse_json_member​函数.

6.2 解析对象中的成员

实现__parse_json_member​函数其实和解析数组的元素的函数相似, 只是对象的成员多了一个字符串类型的键, 在解析value​之前将键解析出来就行了.

static int __parse_json_members(const char *cursor, const char **end, int depth, json_object_t *obj)
{
    int ret;
    int cnt = 0;
    json_member_t *memb;

    while (isspace(*cursor))
    {
        cursor++;
    }

    if (*cursor == '}')
    {
        *end = cursor + 1;
        return 0;
    }

    while (1)
    {
        if (*cursor != '\"')
        {
            return -2;
        }

        cursor++;
        ret = __json_string_length(cursor);
        if (ret < 0)
        {
            return ret;
        }

        memb = (json_member_t *)malloc(offsetof(json_member_t, name) + ret + 1);
        if (memb == NULL)
        {
            return -1;
        }

        ret = __parse_json_member(cursor, &cursor, depth, memb);
        if (ret < 0)
        {
            free(memb);
            return ret;
        }

        __insert_json_member(memb, obj->head.prev, obj);
        cnt++;

        while (isspace(*cursor))
        {
            cursor++;
        }

        if (*cursor == ',')
        {
            cursor++;
            while (isspace(*cursor))
            {
                cursor++;
            }
        }
        else if (*cursor == '}')
        {
            break;
        }
        else
        {
            return -2;
        }
    }

    *end = cursor + 1;
    return cnt;
}

static int __parse_json_member(const char *cursor, const char **end, int depth, json_member_t *memb)
{
    int ret;

    ret = __parse_json_string(cursor, &cursor, memb->name);
    if (ret < 0)
    {
        return ret;
    }

    while (isspace(*cursor))
    {
        cursor++;
    }

    if (*cursor != ':')
    {
        return -2;
    }

    cursor++;
    while (isspace(*cursor))
    {
        cursor++;
    }

    ret = __parse_json_value(cursor, &cursor, depth, &memb->value);
    if (ret < 0)
    {
        return ret;
    }

    *end = cursor;
    return 0;
}

可以看到, 除了多解析了一个string类型的键name​之外, 其他的大部分和数组的解析类似, 不过将单个成员的解析另立了一个函数, 没有太大区别.

在对象的单个成员解析完毕后, 进行了一个__insert_json_member​的操作, 这里就是将解析出来的memb​添加到对象的红黑树和链表之中.

static int __insert_json_member(json_member_t *memb, struct list_head *pos, json_object_t *obj)
{
    struct rb_node **p = &obj->root.rb_node;
    struct rb_node *parent = NULL;
    json_member_t *entry;

    while (*p)
    {
        parent = *p;
        entry = rb_entry(*p, json_member_t, node);
        if (strcmp(memb->name, entry->name) < 0)
        {
            p = &(*p)->rb_left;
        }
        else
        {
            p = &(*p)->rb_right;
        }
    }

    rb_link_node(&memb->node, parent, p);
    rb_insert_color(&memb->node, &obj->root);
    list_add(&memb->list, pos);
}

这里默认对红黑树有基础, 先从父节点开始遍历, 查找memb​适合插入的节点位置, 找到合适的位置后, 进行插入和自平衡操作即rb_link_node​和rb_insert_color​, 最后将memb​也关联到一个链表一样, 操作和数组插入相同, 不过这个是插入到了链表的头结点之后而非尾结点之后.

到这里JSON数据的所有类型解析都实现了, 最后看看destroy相关函数的实现.

6.3 资源释放

在JSON数组和对象的解析中, 如果解析失败, 需要将分配的内存合理释放, 由于数组和对象结构是嵌套结构, 所以需要实现销毁数组元素和对象的成员的函数.

static void __destroy_json_elements(json_array_t *arr)
{
    struct list_head *pos, *tmp;
    json_element_t *elem;

    list_for_each_safe(pos, tmp, &arr->head)
    {
        elem = list_entry(pos, json_element_t, list);
        __destroy_json_value(&elem->value);
        free(elem);
    }
}

static void __destroy_json_value(json_value_t *val)
{
    switch (val->type)
    {
    case JSON_VALUE_STRING:
        free(val->value.string);
        break;
    case JSON_VALUE_ARRAY:
        __destroy_json_elements(&val->value.array);
        break;
    case JSON_VALUE_OBJECT:
        __destroy_json_members(&val->value.object);
        break;
    }
}

static void __destroy_json_members(json_object_t *obj)
{
    struct list_head *pos, *tmp;
    json_member_t *memb;

    list_for_each_safe(pos, tmp, &obj->head)
    {
        memb = list_entry(pos, json_member_t, list);
        __destroy_json_value(&memb->value);
        free(memb);
    }
}

释放的函数其实很简单, 遍历对应的链表, 然后调用销毁value的函数, 销毁value的函数内部根据不同的类型执行销毁函数即可.

参考:

[1] workflow 源码解析 : 基础数据结构 rbtree - 知乎 (zhihu.com)

[2] Workflow 源码解析 Json parser :part1 parse - 知乎 (zhihu.com)

[3] 红黑树 - 维基百科,自由的百科全书 (wikipedia.org)

[4] 二叉树 - 维基百科,自由的百科全书 (wikipedia.org)

[5] 二叉树及其作用浅析-腾讯云开发者社区-腾讯云 (tencent.com)

posted @ 2023-08-09 14:25  明天咪西什么  阅读(11)  评论(0编辑  收藏  举报