CJson源码解析之重要的数据结构cJSON


前言

在现代编程中,JSON已经成为了一种非常流行的数据交换格式。它的简洁性和易读性使得开发者可以轻松地在不同的系统和应用之间共享和理解数据。CJson是一个轻量级的JSON解析库,它提供了一种简单和高效的方式来解析和生成JSON数据。本文将深入探讨CJson的源码,特别是它的核心数据结构cJSON。


cJSON结构体

结构体声明

对于CJson,他有一个很重要的结构体cJSON,如下:

typedef struct cJSON
{
    /* next/prev allow you to walk array/object chains. Alternatively, use GetArraySize/GetArrayItem/GetObjectItem */
    struct cJSON *next;
    struct cJSON *prev;
    /* An array or object item will have a child pointer pointing to a chain of the items in the array/object. */
    struct cJSON *child;

    /* The type of the item, as above. */
    int type;

    /* The item's string, if type==cJSON_String  and type == cJSON_Raw */
    char *valuestring;
    /* writing to valueint is DEPRECATED, use cJSON_SetNumberValue instead */
    int valueint;
    /* The item's number, if type==cJSON_Number */
    double valuedouble;

    /* The item's name string, if this item is the child of, or is in the list of subitems of an object. */
    char *string;
} cJSON;

next,prev与child成员

在cJSON库中,nextprevchild是用来表示JSON对象或数组的内部结构的。示例Json:

{
    "A":10,
    "B":true,
    "C":"Hello World",
    "D":[1,2,3,4],
    "F":{
        "AF":"AF"
    }
}

在cJSON中,这个JSON对象会被表示为一个链表,其中每个键值对都是链表中的一个节点。每个节点都有nextprev成员,分别指向链表中的下一个和上一个节点。

例如,节点"A"的next成员会指向节点"B",节点"B"的prev成员会指向节点"A"。节点"F"的prev成员会指向节点"D",而它的next成员会是NULL,因为它是链表的最后一个节点。

对于数组和嵌套的JSON对象,child成员会被用来指向它们的第一个元素或键值对。例如,节点"D"的child成员会指向它的第一个元素,即1。节点"F"的child成员会指向它的第一个键值对,即"AF":“AF”。
那么数组就是这样:

"D": [1, 2, 3, 4]

这个数组会被表示为一个链表,其中每个元素都是链表中的一个节点。节点"D"的child成员会指向链表的第一个节点,即1。然后,1节点的next成员会指向下一个节点2,2节点的next成员会指向下一个节点3,以此类推,直到最后一个节点4,其next成员为NULL,表示它是链表的结束。同时,每个节点的prev成员会指向它的前一个节点,形成一个双向链表。

type成员

type成员是int类型的,用来标识当前item是什么
他有下面这些取值:

#define cJSON_Invalid (0) /* 无效的JSON数据,例如:"" */
#define cJSON_False  (1 << 0) /* JSON布尔值false,例如:"key": false */
#define cJSON_True   (1 << 1) /* JSON布尔值true,例如:"key": true */
#define cJSON_NULL   (1 << 2) /* JSON null值,例如:"key": null */
#define cJSON_Number (1 << 3) /* JSON数字,例如:"key": 10 */
#define cJSON_String (1 << 4) /* JSON字符串,例如:"key": "value" */
#define cJSON_Array  (1 << 5) /* JSON数组,例如:"key": [1, 2, 3] */
#define cJSON_Object (1 << 6) /* JSON对象,例如:"key": {"subkey": "value"} */
#define cJSON_Raw    (1 << 7) /* raw json,例如:"key": "{raw json string}" */

其他的很好理解,唯一迷惑的就是cJSON_RawcJSON_String不是一个玩意吗?
但是这是错误的理解。
cJSON_String代表了JSON字符串。例如,"key": "value"中的"value"就是一个JSON字符串。
cJSON_Raw则用于表示原始的JSON数据。这通常用于当你想直接插入一段未经处理的JSON数据到你的JSON对象或数组中。例如,"key": "{raw json string}"中的"{raw json string}"就是一个raw JSON数据。

那么什么又叫做未经处理的Json数据???
“未经处理的字符串"通常指的是原始的、未经任何修改或格式化的字符串。在JSON的上下文中,当我们说到"未经处理的JSON数据"或"原始的JSON数据”,我们指的是一段直接从源头获取的、未经任何解析或转换的JSON字符串。

例如,考虑以下的JSON字符串:

"{\"name\": \"John\", \"age\": 30, \"city\": \"New York\"}"

这就是一个"未经处理的字符串"或"原始的JSON数据"。它是一个完整的JSON对象,包含了"name"、"age"和"city"三个键,以及它们对应的值。这个字符串可以直接被JSON解析器解析,转换为一个JSON对象。简而言之就是普通的字符串,他可以被转换为Json对象,但没有转换之前的状态

相反,如果我们有一个字符串"John",并且我们想把它作为一个JSON字符串的一部分,我们需要对它进行处理,例如,将它包裹在引号中,转义必要的字符等,使得它能够被正确地解析为JSON的一部分。

数据成员

这些是cJSON库中的一些重要成员,它们在cJSON数据结构中存储了关于JSON数据的关键信息。以下是每个成员的详细介绍:

  • char *valuestring:如果cJSON项的类型是cJSON_StringcJSON_Raw,那么valuestring成员就会存储该项的字符串值。例如,在JSON数据{"key": "value"}中,"value"就会被存储在valuestring成员中。

  • int valueint:这个成员已经被弃用,不推荐直接写入valueint。如果需要设置一个cJSON项的数值,应该使用cJSON_SetNumberValue函数。

  • double valuedouble:如果cJSON项的类型是cJSON_Number,那么valuedouble成员就会存储该项的数值。例如,在JSON数据{"key": 10.5}中,10.5就会被存储在valuedouble成员中。

  • char *string:如果一个cJSON项是一个对象的子项,或者是一个对象的子项列表中的一项,那么string成员就会存储该项的名称。例如,在JSON数据{"key": "value"}中,"key"就会被存储在string成员中。


总结

通过深入研究CJson的源码,我们可以更好地理解JSON数据格式的内在工作原理,以及如何有效地解析和生成JSON数据。cJSON作为CJson的核心数据结构,它的设计和实现对于理解CJson的功能和性能至关重要。希望通过本文的阅读,你能对CJson有一个更深入的理解,也能在你的编程实践中更有效地使用JSON数据格式。未来,我们还将继续探索更多关于CJson的主题,包括它的性能优化,错误处理,以及如何在特定的应用场景中使用CJson。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
假设需要解析JSON数据如下: ``` { "name": "John", "age": 30, "city": "New York", "hobbies": ["reading", "traveling", "swimming"], "education": { "degree": "Master", "major": "Computer Science" } } ``` 使用cJSON库进行解析的示例代码如下: ```c #include <stdio.h> #include <stdlib.h> #include "cJSON.h" int main() { char *json_str = "{\"name\":\"John\",\"age\":30,\"city\":\"New York\",\"hobbies\":[\"reading\",\"traveling\",\"swimming\"],\"education\":{\"degree\":\"Master\",\"major\":\"Computer Science\"}}"; cJSON *root = cJSON_Parse(json_str); if (root == NULL) { printf("Error before: [%s]\n", cJSON_GetErrorPtr()); return 1; } cJSON *name = cJSON_GetObjectItemCaseSensitive(root, "name"); if (cJSON_IsString(name) && (name->valuestring != NULL)) { printf("Name: %s\n", name->valuestring); } cJSON *age = cJSON_GetObjectItemCaseSensitive(root, "age"); if (cJSON_IsNumber(age)) { printf("Age: %d\n", age->valueint); } cJSON *city = cJSON_GetObjectItemCaseSensitive(root, "city"); if (cJSON_IsString(city) && (city->valuestring != NULL)) { printf("City: %s\n", city->valuestring); } cJSON *hobbies = cJSON_GetObjectItemCaseSensitive(root, "hobbies"); if (cJSON_IsArray(hobbies)) { int i, size = cJSON_GetArraySize(hobbies); printf("Hobbies:\n"); for (i = 0; i < size; i++) { cJSON *item = cJSON_GetArrayItem(hobbies, i); if (cJSON_IsString(item) && (item->valuestring != NULL)) { printf(" %s\n", item->valuestring); } } } cJSON *education = cJSON_GetObjectItemCaseSensitive(root, "education"); if (cJSON_IsObject(education)) { cJSON *degree = cJSON_GetObjectItemCaseSensitive(education, "degree"); if (cJSON_IsString(degree) && (degree->valuestring != NULL)) { printf("Education - Degree: %s\n", degree->valuestring); } cJSON *major = cJSON_GetObjectItemCaseSensitive(education, "major"); if (cJSON_IsString(major) && (major->valuestring != NULL)) { printf("Education - Major: %s\n", major->valuestring); } } cJSON_Delete(root); return 0; } ``` 运行结果: ``` Name: John Age: 30 City: New York Hobbies: reading traveling swimming Education - Degree: Master Education - Major: Computer Science ``` 解析过程中,先使用cJSON_Parse函数将JSON字符串解析成一个cJSON对象(即root)。然后,使用cJSON_GetObjectItemCaseSensitive函数分别获取其中的各个成员对象,并使用不同的cJSON类型判断函数(如cJSON_IsString、cJSON_IsNumber、cJSON_IsArray等)判断其类型。最后,使用各自的value获取成员对象的值。最后,使用cJSON_Delete函数释放cJSON对象。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

人才程序员

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

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

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

打赏作者

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

抵扣说明:

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

余额充值