cJSON源码解析之add_item_to_object函数


前言

在我们的日常编程中,JSON已经成为了一种非常常见的数据交换格式。在C语言中,我们通常使用cJSON库来处理JSON数据。cJSON库提供了一系列的函数来创建、解析和操作JSON数据。其中,add_item_to_object函数是一个关键的函数,它允许我们将一个新的项目添加到一个已存在的JSON对象中。在这篇文章中,我们将深入探讨add_item_to_object函数的内部实现。


add_item_to_object函数是干什么的

这个函数用于把一个item添加到一个对象里面
他是cJSON_AddItemToObjectcJSON_AddItemToObjectCS函数的实现
在这里插入图片描述

add_item_to_object代码解析

函数实现

static cJSON_bool add_item_to_object(cJSON * const object, const char * const string, cJSON * const item, const internal_hooks * const hooks, const cJSON_bool constant_key)
{
    char *new_key = NULL;
    int new_type = cJSON_Invalid;

    if ((object == NULL) || (string == NULL) || (item == NULL) || (object == item))
    {
        return false;
    }

    if (constant_key)
    {
        new_key = (char*)cast_away_const(string);
        new_type = item->type | cJSON_StringIsConst;
    }
    else
    {
        new_key = (char*)cJSON_strdup((const unsigned char*)string, hooks);
        if (new_key == NULL)
        {
            return false;
        }

        new_type = item->type & ~cJSON_StringIsConst;
    }

    if (!(item->type & cJSON_StringIsConst) && (item->string != NULL))
    {
        hooks->deallocate(item->string);
    }

    item->string = new_key;
    item->type = new_type;

    return add_item_to_array(object, item);
}

函数原理解析

开头的代码

首先他声明了两个变量,用于存储key值和该键值对的类型的

char *new_key = NULL;
int new_type = cJSON_Invalid;

紧接着,他去判断参数的合法性,为了内存的异常安全:‘

if ((object == NULL) || (string == NULL) || (item == NULL) || (object == item))
{
    return false;
}

constant_key参数的作用

在cJSON库中,constant_key参数是一个布尔值,它决定了键(key)是进行深拷贝还是浅拷贝。

如果constant_keytrue,那么在添加新项目到JSON对象时,键(key)将会进行浅拷贝,也就是直接复制指针。这意味着,如果原始的键字符串在以后被修改或释放,那么存储在JSON对象中的键也会受到影响。

如果constant_keyfalse,那么键(key)将会进行深拷贝,也就是复制整个字符串的内容到新的内存位置。这样,即使原始的键字符串在以后被修改或释放,也不会影响到存储在JSON对象中的键。

可以看到他的浅拷贝和深拷贝是完全不同的:
在这里插入图片描述

if (constant_key)
{
    new_key = (char*)cast_away_const(string);
    new_type = item->type | cJSON_StringIsConst;
}
else
{
    new_key = (char*)cJSON_strdup((const unsigned char*)string, hooks);
    if (new_key == NULL)
    {
        return false;
    }

    new_type = item->type & ~cJSON_StringIsConst;
}

在浅拷贝,直接把参数的key复制到new_key里面,然后把new_type赋值成对应的类型
那么他为何要| cJSON_StringIsConst
这行代码中的|操作符是C语言中的位运算符,表示按位或操作。cJSON_StringIsConst是一个常量,它的值通常为0x200,在二进制中表示为10 0000 0000

当我们执行item->type | cJSON_StringIsConst时,实际上是将item->type的值和cJSON_StringIsConst的值进行按位或操作。这样做的目的是为了将item->type的第10位设置为1,而不改变其他位的值。

这里cJSON_StringIsConst的作用是标记字符串是否为常量。如果一个字符串被标记为常量,那么在cJSON对象被删除时,这个字符串就不会被释放。这对于那些指向静态或全局变量的字符串非常有用,因为这些字符串不能被释放。

所以,new_type = item->type | cJSON_StringIsConst;这行代码的作用就是将item->type的值更新为新的类型,同时保留了原来的类型信息,并标记字符串为常量。

在深拷贝中,使用了cJSON_strdup函数复制字符串,我们上篇文章已经介绍过
new_type = item->type & ~cJSON_StringIsConst;类比上面,这段代码也就是把这个字符串标记为不是常量,在删除的时候可以直接删除

最后的if判断

这段代码的目的是在添加新的键值对到JSON对象时,安全地处理旧的键字符串。

if (!(item->type & cJSON_StringIsConst) && (item->string != NULL))
{
    hooks->deallocate(item->string);
}

!(item->type & cJSON_StringIsConst)这段的含义如下:
这部分是检查item->type是否被标记为cJSON_StringIsConst。如果被标记为cJSON_StringIsConst,那么这个字符串就是一个常量字符串,我们不能释放它。所以,我们使用!操作符来检查item->type是否没有被标记为cJSON_StringIsConst

(item->string != NULL):这部分是检查item->string是否为NULL。如果item->string为NULL,那么我们就没有必要(也不能)释放它。

最后就把参数item复制上我们的状态就行了:

item->string = new_key;
item->type = new_type;

我们会发现,这个函数仅仅是把键和type安装到参数type上面,但是还没有进行item安装到object上面,所以这里又出现一个函数,他专门用于链表的操作的add_item_to_array

add_item_to_array函数

add_item_to_array函数代码如下:

static cJSON_bool add_item_to_array(cJSON *array, cJSON *item)
{
    cJSON *child = NULL;

    if ((item == NULL) || (array == NULL) || (array == item))
    {
        return false;
    }

    child = array->child;
    /*
     * To find the last item in array quickly, we use prev in array
     */
    if (child == NULL)
    {
        /* list is empty, start new one */
        array->child = item;
        item->prev = item;
        item->next = NULL;
    }
    else
    {
        /* append to the end */
        if (child->prev)
        {
            suffix_object(child->prev, item);
            array->child->prev = item;
        }
    }

    return true;
}

他的主要代码如下:

if (child == NULL)
{
    /* list is empty, start new one */
    array->child = item;
    item->prev = item;
    item->next = NULL;
}
else
{
    /* append to the end */
    if (child->prev)
    {
        suffix_object(child->prev, item);
        array->child->prev = item;
    }
}

他的图例:

  1. 如果链表为空 (child == NULL),那么新的item就会成为链表的第一个(也是唯一的)元素。这个情况可以用下面的字符画来表示:
array
  |
  v
 item  --> NULL
  ^
  |
 item

在这个图中,array->child指向itemitem->prev指向item自身,item->next指向NULL

  1. 如果链表不为空,那么新的item就会被添加到链表的末尾。这个情况可以用下面的字符画来表示:
array
  |
  v
 child1 <--> child2 <--> ... <--> childN <--> item --> NULL
  ^                                          ^
  |                                          |
 child1                                    item

在这个图中,array->child指向链表的第一个元素child1childN->next指向新的itemitem->prev指向childNitem->next指向NULL

这样就成功的把我们的item连接到object里面了


总结

通过深入研究add_item_to_object函数的源码,我们可以更好地理解cJSON库是如何处理JSON对象的。这个函数的实现虽然简单,但却非常关键,它使得我们可以方便地向JSON对象中添加新的项目。希望这篇文章能帮助你更好地理解cJSON库的内部工作原理,以及如何在你自己的项目中使用它。

  • 7
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

人才程序员

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

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

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

打赏作者

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

抵扣说明:

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

余额充值