要做存储业务,我解析了一个项目的源码

news2025/1/16 1:39:11

最近在做存储相关的业务,更具体的来说是存储相关的研发,于是就上网查了一下相关的资料,思虑再三打算从最简单的 Json 数据交换格式开始研究。

JSON是独立于编程语言的数据交换格式,几乎所有与网络开发相关的语言都有JSON函数库,例如:C语言里的cJSON,Golang语言里的package json,Java语言里的jackson,Python语言中的pyson等。

1、JSON介绍

2、cJSON源码分析

3、如何设计“key-value”的数据结构

4、如何设计JSON节点操作函数

5、如何设计解析JSON字符串函数

6、如何设计JSON节点转JSON字符串

1、JSON介绍

可能有些还是比较还在读书的学弟学妹还不知道什么是JSON,有什么约定,这里就大致介绍一下,已经了解的可以直接跳过该部分。

要开发JSON编解码器,当然要了解一下什么是JSON

JSON基本数据格式采用的是“键值对”,即“key : value”形式,其中key只能是字符串,而value的数据类型有多种;多个“键值对”之间使用逗号,分隔,键与值之间用冒号:分隔

JSON的value基本数据类型:

  1. 数值(number):十进制数,不能有前导0,可以为负数,可以有小数部分。不区分整数与浮点数

  2. 字符串(string):以双引号""括起来的零个或多个Unicode码位。支持反斜杠开始的转义字符序列

  3. 对象(object):由无序的“键值对”组成,使用花括号{}包裹

  4. 数组(array):有序的零个或者多个值,每个值可以为任意类型,使用方括号包裹,形如:[value, value]

  5. true:布尔值true

  6. false:布尔值false

  7. null:空值

四个特定字符被认为是空白符:空格符(' ')、水平制表符('\t')、回车符('\r')、换行符('\n')

JSON字符串中,在元素前后允许存在无意义的空白符(会被忽略)

JSON字符串示例

{
  "firstName": "John",
  "lastName": "Smith",
  "isAlive": true,
  "age": 27,
  "address": {
    "streetAddress": "21 2nd Street",
    "city": "New York",
    "state": "NY",
    "postalCode": "10021-3100"
  },
  "children": [
    "Catherine",
    "Thomas",
    "Trevor"
  ],
  "phoneNumbers": [
    {
      "type": "home",
      "number": "212 555-1234"
    },
    {
      "type": "office",
      "number": "646 555-4567"
    }
  ],
  "spouse": null
}

2、cJSON源码分析

这里选择cJSON做进一步了解和解析,因为C语言是编程语言之母,也是我的第一门语言。

013b9037753f420e64e493f899046da2.png
cJSON仓库

cJSON是github上一个star数为8.8k的仓库,是很多C/C++从业者开始学习源码知识都会开始选择的一个项目。

本文将对cJSON源码进行一个介绍,碍于篇幅原因写不了太多东西,这里只写一些源码解析过程中的宏观介绍。即使是这样,这篇文章也有将近5900 字之多。

3f96fe5e3e3ef41ecf944af53f30e111.png

所以具体的实现细节可以参考附详细注释的源码仓库:https://github.com/MingWangSong/MycJSON

我们了解清楚JSON数据的约定后,即可边提设计需求边分析cJSON是如何实现,

JSON编解码工具包主要提供JSON序列化和反序列化的这个两大块功能。

其中,序列化是指将编程语言结构化数据转化为JSON字符串;反序列化则是指相反的操作。

表述形象一点,JSON序列化就是输出或者打印JSON字符串,JSON反序列化操作是为了解析JSON字符串。

97b418738d63bd3797c9a1c177b96592.png
JSON序列化与反序列化

JSON数据整体就是一个对象类型的数据,可以假象成一个key缺省的根节点;将一个“key-value”视为JSON中的基本结构,整个JSON中处处为“key-value”(这句式有Linux味)。

因此,要想实现JSON序列化和反序列化,则需定义“key-value”的数据结构,该结构称之为JSON节点

3、如何设计“key-value”的数据结构

  1. “key-value”数据结构的设计需要考虑两个事情:单一“key-value”的数据表示多个“key-value”之间的关系表示

  2. 针对多个“key-value”之间的关系表示,cJSON的设计想法是通过链表来串联JSON节点,具体来讲,使用单链表实现JSON嵌套关系,使用双向链表实现JSON同级关系。

    因此,后续的所有JSON节点操作都很链表相关。下面举例绘图说明:

    {
      "firstName": "John",
      "lastName": "Smith",
      "children": [
        "Catherine",
        "Thomas",
        "Trevor"
      ],
      "address": {
        "streetAddress": "2nd-Street",
        "city": "New York"
      }
    }

    cJSON处理上述JSON数据的结构示意图如下:

    755e9435f2ea3817a42495a6b4cfd867.png

    在上图第一列中,黄色块为JSON根节点,该节点key缺省,value对应JSON字符串最外围{}包裹的若干个JSON节点,这些节点为根节点的子节点,图中红色箭头代表指向子节点的指针;

    第二列代表根节点下的一级JSON子节点,同级节点构成双链表,图中黑色指针为双链表的前驱和后继指针。

    值得注意的是,cJSON处理数组对象时,是将数组中每个元素视为一个JSON节点存储,同样采用双链表的形式存储,数组类型的JSON节点的value是没有值的,而是通过指向子节点的指针来存储数组地址,因此对象类型节点和数组类型节点的后续很多操作都可复用,非常的妙!

  3. cJSON节点数据结构设计如下:

    typedef struct cJSON {
        struct cJSON *next;  // 同级节点后继指针
        struct cJSON *prev;  // 同级节点前驱指针
        struct cJSON *child; // 子节点指针
        int type;            // “value”的类型,一共有9种类型
        char *valuestring;   // “value”类型为字符串对应的存值变量
        int valueint;        // “value”类型为整数对应的存值变量(写入valueint已弃用)
        double valuedouble;  // “value”类型为浮点数对应的存值变量
        char *string;        // 键值对中的“key”值
    } cJSON;

    其中,next,prev,child分别代表前驱节点、后继节点和子节点,用于处理JSON数据节点间的层级关系;type表示该JSON节点的value类型。特别注意,变量string代表着key(这个命名让我很不爽)。

    因为类型定义中区分了布尔值的true和false两种类型,所以布尔类型的JSON节点实际不需要其他变量存储value值。具体类型定义如下:

    #define cJSON_Invalid (0)       // 非法类型
    #define cJSON_False (1 << 0)
    #define cJSON_True (1 << 1)
    #define cJSON_NULL (1 << 2)
    #define cJSON_Number (1 << 3)
    #define cJSON_String (1 << 4)
    #define cJSON_Array (1 << 5)
    #define cJSON_Object (1 << 6)
    #define cJSON_Raw (1 << 7)      // raw json
    #define cJSON_IsReference (1 << 8)
    #define cJSON_StringIsConst (1 << 9)

4、如何设计JSON节点操作函数

无论是JSON序列化还是反序列化,都无法避免针对JSON节点的操作,因此先了解cJSON中JSON节点操作设计

  1. 创建JSON节点

    4ca9d10430e2b8e10b2a72284c7f27ec.png

    上图为cJSON中创建JSON节点的调用流程图。根据cJSON源码实现方式,创建JSON结点函数可大致分为三类:创建非数组类型JSON节点,创建数组类型JSON节点,创建JSON节点引用。由于同类函数实现逻辑相似。

    创建JSON节点拢共分三步:开辟空间,设置节点类型,设置节点value。

    注意,创建节点的时候不会设置key值,因此要新增节点的时候不会直接调用此类函数,而是提供了对应的Add函数,后文将会详细介绍。

  • 创建非数组类型JSON节点

    创建非数组类型节点时,先调用cJSON_New_Item()为cJSON节点申请空间并用memset进行初始化,然后再设置节点类型和value。如果是添加指定的节点到指定的对象或者数组,则需设置nextchild指针,源码如下:

    cJSON * cJSON_CreateObjectReference(const cJSON *child){
        cJSON *item = cJSON_New_Item(&global_hooks);
        if (item != NULL) {
            item->type = cJSON_Object | cJSON_IsReference;
            item->child = (cJSON*)cast_away_const(child);
        }
        return item;
    }
    cJSON * cJSON_CreateArrayReference(const cJSON *child) {
        cJSON *item = cJSON_New_Item(&global_hooks);
        if (item != NULL) {
            item->type = cJSON_Array | cJSON_IsReference;
            item->child = (cJSON*)cast_away_const(child);
        }
        return item;
    }
  • 创建数组类型JSON节点

    创建数组类型节点时,先调用cJSON_New_Item()为cJSON节点申请空间并用memset进行初始化,然后再设置节点类型,接着,需要根据数组数组构建子节点链表,子节点链表是双向链表,并且头结点前驱指针指向尾结点,以便尾插入的时候快速定位尾结点。

新增JSON节点

93728fc430e6624660ceb13fc63e7f83.png

由于cJSON中采用链表结构存储节点,因此新增JSON节点涉及的是链表中的插入操作。因为在cJSON中,value中的数组元素也是采用JSON节点来存储的,所以在数组用添加元素也是一样的链表操作。

由上图可知,新增JSON节点的核心函数是add_item_to_array(),该方法已尾插法的形式插入新建的JSON节点,为了能快速找到尾结点,每次插入时会更新头结点的前驱指针指向尾结点,具体插入过程如下图所示。

cJSON还提供了cJSON_InsertItemInArray(),该函数实现了在数组任意位置插入元素。

01ba8db6f1a7587463d225cf18cc3de6.png

替换JSON节点

9b4b8736d309d0ae733cc7cbeae06eaa.png

替换JSON节点的实现核心函数是cJSON_ReplaceItemViaPointer,主要是双链表中元素的替换操作。

删除JSON节点

89570931f49215011e9e461b55f8a463.png

删除JSON节点的核心函数是cJSON_DeletecJSON_DetachItemViaPointer,递归删除指定节点及其所有子节点,但是cJSON_IsReference类型的节点不会删除。

其中,cJSON_DetachItemViaPointer主要处理待删除节点的相关指针;cJSON_Delete主要用于释放删除后的节点存储空间。

查找JSON节点

ac8cc87a07c0e1370f2a2a84cc52bb08.png

查找JSON节点的核心函数是get_object_item,由于是链表存储结构,因此只能遍历查找。

5、如何设计解析JSON字符串函数

4bec8bc81a55cb57262c31232ff3b514.png

使用cJSON来解析JSON节点时,直接调用cJSON_Parse()函数,上图展示该函数的调用关系,核心函数是parse_valueparse_object。其中,parse_value实现了分别解析不同的类型的value值,布尔类型和null类型直接在本函数中处理,数字、字符串、数组、对象类型通过封装函数分别处理;parse_object主要实现了针对JSON节点的解析,通过先借助parse_string解析key值,再通过parse_value解析value值。

特别地,因为cJSON中嵌套对象和数组的数据结构设计是一样的,因此parse_objectparse_array的实现几乎一样,区别主要在于parse_array没有key值需要解析。

6、如何设计JSON节点转JSON字符串函数

32592c519dadaffbea46aee8fcbca48b.png

对比解析JSON字符串函数的函数调用图可以发现,JSON节点转JSON字符串函数的设计逻辑类似,也包含两个核心函数print_valueprint_object,这里就不做更进一步的解析了。

希望今天的源码解析能对大家有帮助,以后会带来更多的源码级项目解析。


参考链接

  • DaveGamble/cJSON: Ultralightweight JSON parser in ANSI C (github.com):https://github.com/DaveGamble/cJSON

  • JSON - 维基百科,自由的百科全书 (wikipedia.org):https://zh.wikipedia.org/zh-cn/JSON

  • JSON:https://www.json.org/json-zh.html

  • cJSON源码及解析流程详解_Tyler_Zx的博客-CSDN博客:https://blog.csdn.net/qq_38289815/article/details/103307262

  • 如何解析JSON数据及内存钩子的使用方法-腾讯云开发者社区-腾讯云 (tencent.com):https://cloud.tencent.com/developer/beta/article/1662821

  • cJSON更换默认的malloc函数 - chilkings - 博客园 (cnblogs.com):https://www.cnblogs.com/chilkings/p/15821140.html

  • cjson库的使用以及源码阅读 - cfzhang - 博客园 (cnblogs.com):https://www.cnblogs.com/cfzhang/p/99da02ab2f02520d458c415a5314f83d.html

  • C语言中.h和.c文件解析(很精彩)-腾讯云开发者社区-腾讯云 (tencent.com):https://cloud.tencent.com/developer/article/1690858

  • MingWangSong/MycJSON (github.com):https://github.com/MingWangSong/MycJSON

↓↓推荐↓↓↓

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/557603.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

chatgpt赋能Python-python_figsize

Python figsize&#xff1a;图形大小的更改 在Python数据可视化中&#xff0c;通过更改图形的大小可以使得图形更易于阅读和理解。绘图函数的“figsize”参数可以让您控制图形的大小。本文将探讨figsize的含义、使用示例以及如何根据您的需要精确调整图形大小。 什么是figsiz…

【CSAPP】虚拟内存(VM)

&#x1f4ad; 写在前面&#xff1a;本文将学习《深入理解计算机系统》虚拟内存部分&#xff0c;CSAPP 是计算机科学经典教材《Computer Systems: A Programmers Perspective》的缩写&#xff0c;该教材由Randal E. Bryant和David R. OHallaron 合著。 &#x1f4dc; 本章目录…

【Python】判断语句 ④ ( 判断语句嵌套 )

文章目录 一、判断语句嵌套1、语法说明2、代码示例 一、判断语句嵌套 1、语法说明 在 Python 的开发场景中 , 除了 单个条件判定 : if 条件判定 , if else 条件判定 ;多个并列条件判定 : if elif else 条件判定 ; 之外 , 还有 满足 前置条件判定 后 进行 第二次条件判定 的开…

C#,码海拾贝(21)——线性方程组求解的全选主元高斯消去法之C#源代码,《C#数值计算算法编程》源代码升级改进版

using System; namespace Zhou.CSharp.Algorithm { /// <summary> /// 求解线性方程组的类 LEquations /// 原作 周长发 /// 改编 深度混淆 /// </summary> public static class LEquations { /// <summary> /…

chatgpt赋能Python-python_geany

了解Python Geany: 一种强大且高效的Python IDE 介绍 Python Geany是一种非常流行的Python集成开发环境&#xff08;IDE&#xff09;&#xff0c;是由Geany团队开发的。它是一种跨平台的开发工具&#xff0c;可在Windows、Linux和macOS等不同平台上运行。Python Geany提供了支…

Linux常用命令——host命令

在线Linux命令查询工具 host 常用的分析域名查询工具 补充说明 host命令是常用的分析域名查询工具&#xff0c;可以用来测试域名系统工作是否正常。 语法 host(选项)(参数)选项 -a&#xff1a;显示详细的DNS信息&#xff1b; -c<类型>&#xff1a;指定查询类型&am…

小白畅玩免费支持ChatGPT3和4,一键定制自己专属AI

AI爆炸时代。你不用AI&#xff0c;别人就会用AI,当别人用AI,为自己给工作生活赋能时候。生活自在&#xff0c;工作高效&#xff0c;AI正在潜移默化改变我们生活 基于OPEN AI平台 轻松让AI 为你的生活赋能. 总之我现在生活已经离不开AI&#xff0c;帮助了。 不管是工作上问题…

第四范式涂威威:企业专属大模型技术需闭环数据、思维链学习、高落地效率...

‍ 近日&#xff0c;以“智行天下 能动未来”为主题的第七届世界智能大会隆重举办&#xff0c;第四范式副总裁、主任科学家涂威威出席高峰会&#xff0c;与中国工程院院士邬江兴、德国弗劳恩霍夫电子纳米系统研究所所长Harald Kuhn、高通公司中国区董事长孟樸等院士及企业代表&…

chatgpt赋能Python-python_for_in遍历列表

Python for-in循环遍历列表&#xff1a;最简单易用的方法 Python是最流行的编程语言之一&#xff0c;也是许多开发人员的首选工具。其中&#xff0c;for循环是Python最重要的控制结构之一&#xff0c; for-in循环是其中最常用的形式之一。在本文章中&#xff0c;我们将重点介绍…

做流量卡代理,看看人家是怎么赚钱的?

这两年来&#xff0c;流量卡市场可谓是一片欣欣向荣&#xff0c;三大运营商也推出了多款热门套餐。 ​ 作为上网的主要方式&#xff0c;流量卡在这两年可谓是“风光无限”&#xff0c;上至60岁老人&#xff0c;下至16岁的学生都在使用&#xff0c;就连小编的亲戚朋友也都在网上…

chatgpt赋能Python-python_gi

Python-GI: 一个强大的Python库 Python-GI是一个用于Python的开源库&#xff0c;它提供了一个统一的接口来访问底层的系统库。这个库的目的是方便Python开发人员使用底层操作系统或系统库的底层功能。Python-GI是GNOME桌面环境的一部分&#xff0c;它为Python开发人员提供了访…

因果

数字化转型已经喊了很多年了&#xff0c;但是很多人仍然不明白数字化转型是怎么回事&#xff0c;为啥要转。很多人甚至以为数字化转型就是个营销噱头&#xff0c;还有人跟着瞎起哄什么互联网红利消失、中国供给侧和消费侧人口红利消失等等。 我不讲&#xff0c;很多人都不知道。…

字节跳动开源其云原生数据仓库 ByConity

动手点关注 干货不迷路 ‍ ‍项目简介 ByConity 是字节跳动开源的云原生数据仓库&#xff0c;它采用计算-存储分离的架构&#xff0c;支持多个关键功能特性&#xff0c;如计算存储分离、弹性扩缩容、租户资源隔离和数据读写的强一致性等。通过利用主流的 OLAP 引擎优化&#xf…

吴恩达 x OpenAI Prompt Engineering教程中文笔记

Datawhale干货 作者&#xff1a;刘俊君&#xff0c;Datawhale成员 完整课程&#xff1a;《吴恩达ChatGPT最新课程》 &#x1f433;Reasons & Importance Important for research, discoveries, and advancement 对研究、发现和进步很重要 Accelerate the scientific resea…

案例15:Java餐厅外卖管理系统设计与实现开题报告

博主介绍&#xff1a;✌全网粉丝30W,csdn特邀作者、博客专家、CSDN新星计划导师、java领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精彩专…

一步步教你安装RabbitMQ Server在Ubuntu上,并让其支持远程访问!

文章目录 前言1.安装erlang 语言2.安装rabbitMQ3. 内网穿透3.1 安装cpolar内网穿透(支持一键自动安装脚本)3.2 创建HTTP隧道 4. 公网远程连接5.固定公网TCP地址5.1 保留一个固定的公网TCP端口地址5.2 配置固定公网TCP端口地址 转载自cpolar内网穿透的文章&#xff1a;无公网IP&…

针对自主泊车的多相机视觉惯导同时定位与建图方案

文章&#xff1a;Multi-Camera Visual-Inertial Simultaneous Localization and Mapping for Autonomous Valet Parking 作者&#xff1a;Marcus Abate, Ariel Schwartz, Xue Iuan Wong, Wangdong Luo, Rotem Littman, Marc Klinger, Lars Kuhnert, Douglas Blue, Luca Carlone…

chatgpt赋能Python-python_geohash

Python GeoHash: 将位置信息转换为字符串 在现代生活中&#xff0c;我们经常需要处理和分析大量的位置信息&#xff0c;包括地图、GPS、定位等。而一种流行的做法是将位置信息转换为字符串&#xff0c;并使用该字符串进行存储、传输和查询。GeoHash就是一种将位置信息压缩为字…

Tomcat之服务管理页面manager部署

一、tomcat服务管理页面manager简介 Tomcat的管理页面Manager是一个Web应用程序&#xff0c;用于管理Tomcat服务器的部署和操作。它提供了一个易于使用的界面&#xff0c;可以通过Web浏览器访问。Manager可以帮助管理员对Tomcat服务器进行以下操作&#xff1a; 部署/卸载Web应…

chatgpt赋能Python-python_gil

简介 Python&#xff08;特别是 CPython 实现&#xff09;有一个全局解释器锁&#xff08;Global Interpreter Lock&#xff0c;简称 GIL&#xff09;&#xff0c;它的作用是确保只有一个线程可以执行 Python 代码。这让多线程 Python 程序的并发性有所限制。 为什么需要 GIL…