C语言解析json数据结构分析与教程

2023-10-26

写在前面的话:本文档旨在归纳总结个人的学习经验与成果,记录自己的成长,随便给大家分享自己解决的思路,为在这方面有需要的朋友提供一个参考。本人能力有限水平一般,文中难免会有一些错误,希望大家抱着辩证客观的态度来阅读,有错误还请各位海涵包容并予以批评指正。大家要是没兴趣看教程可以直接下载代码测试使用:https://download.csdn.net/download/qq_33784286/14933156

  • 什么是json

我相信这是一句废话,大家既然能搜索到这篇文章,必定是对json有了一定的了解,这里我直接贴出百度上的内容:

JSON(JavaScript Object Notation, JS 对象简谱) 是一种轻量级的数据交换格式。它基于 ECMAScript (欧洲计算机协会制定的js规范)的一个子集,采用完全独立于编程语言的文本格式来存储和表示数据。简洁和清晰的层次结构使得 JSON 成为理想的数据交换语言。 易于人阅读和编写,同时也易于机器解析和生成,并有效地提升网络传输效率。

这说的很笼统,很模糊,至少我认为是这样的,那么我说一下我的理解,我认为json是一种数据交换通信中作为数据包协议的数据交换格式。那么要作为一种通用的数据交换格式他就一定满足特定的特征才能应用在实际项目中。

 

说到这里就不得不提以下json的数据格式了,其实json的数据格式特别简单,相信经过我的总结,大家都能对json格式有一个比较清晰的认识。

Json数据格式解读:“2种数据结构4种数据类型

2种数据结构:对象(object)、数组(arrary

4种数据类型:字符串(string)、整形数字(int)、浮点型数字(double)、布尔(bool

       对象(object:由一对大括号(“{}”)包括起来的一组键值对(键值对:由键名(keyName)和键值(keyValue)构成,在json中,键值可以是“对象”,“数组”,“字符串”,“整形数字”,“浮点型数字”,“布尔”构成),也就是说“对象”这种数据结构的特点就是开始和结尾分别有“{”和“}”来提示“对象”的开始和结束,而对象里的内容是一组组键值对。例如:

{

    "object":"hello world",

    "intNum":123,

    "doubleNum":123.123,

    "bool":true

}

通过上面的例子可以看到键名是由字符串构成,而键名和键值之间是由“:”分隔开,键值对与键值对之间是由“,”区分,在对象的最后一个键值对后面无需加上“,,这些都是json中特定的格式,可以用来判断json字符串是否合法。

       数组(arrary:由一对中括号(“[]”)包括起来的一组键值,注意这里讲的是键值而不是键值对,因为在数组中每个元素的位置可以由下标来表示,所以键名在这显得特别多余,因此在数组中只要键值而不是键值对。例如:

[

    "hello world",

    123,

    123.123,

    true

]

同样的,在数组中,键值与键值间由“,”区分,最后一个键值后面无需加上“,”。

对象和数组可以相互包括彼此,一般来说不限制嵌套深度。例如:

{

    "object":"json object",

    "time":"2021.01.25",

    "address":"guangZhou,guangDong",

    "name":"renJia Lu",

    "object-test":

    {

        "string":"hello world",

        "intNum":123456,

        "doubleNum":3.141592,

        "bool_1":true,

        "bool_2":false,

        "arrary":

        [

            "this is object.table-string",

            857857,

            857.857,

            true,

            false,

            [

                "this is object.table.table-string",

                996996,

                996.996,

                true,

                false

            ]

        ]

    },

    "table-test":

    [

        "this is table-string",

        886886,

        886.886,

        false,

        true,

        {

            "string":"this is table.object-string",

            "intNum":123456,

            "doubleNum":3.141592,

            "bool_1":true,

            "bool_2":false

        }

    ]

}

相信看到这里,大家都对json的数据结构都有了个大概,那么接下来我们开始来将这一段json格式的字符串解析成json格式的数据结构。

  • 解析json字符串

很容易想到json数据结构与咱们C语言中的结构体比较像,那么我们可以用结构体来替代json数据结构,那么咱们的结构体要有哪些元素呢?这里一步步将怎么来的太麻烦了,我直接贴出我定义的结构体,带着答案来给大家简单分析一下。

typedef struct cJSON   //cjson基本结点结构体

{

    struct cJSON* next,*last;   

    struct cJSON* child;

    unsigned char *keyName;

    Json_type keyValueType;

    unsigned char *valueString;

    int       valueInt;

    double    valueDouble;

    Json_Bool_type valueBool;

}cJSON,*cJSON_P;

next        表示下一个键值对的指针(其实是链表下一个结点的指针)

last                表示上一个键值对的指针(其实是链表上一个结点的指针)

child        用于对象或者数组的子结点(稍后解释)

keyName      键值名

keyValueType 键值类型(7 稍后解释)

valueString  用于存储字符串型键值

valueInt     用于存储整形数字键值

valueDouble  用于存储浮点型数字数值

valueBool    用于存储布尔型值

 

可能大家看到这里有点疑问了,还要用到链表?答案是的,因为我们在解析前对该json字符串是出于一种未知状态的,我们不知道他需要多大空间,不知道他的键值对有多少等等等等问题,那么面对这种问题,我们的解决策略就是采用链表来处理。我们的解决思路是一个键值对就是链表中的一个结点,然后通过nextlast指针来链接其整个数据结构,而其中还有对象或者数组这种嵌套的数据我们通过引入一个child指针来解决,也就是说在解析过程中把对象和数组也当成一个键值类型来处理,那么可能又有朋友要问了,及时把对象和数组当成键值类型来处理也只有6中键值类型啊,上面为什么说有7种键值类型啊?其实我没有讲错,我在做程序的时候考虑到一种空值就是NULL。接下来看看我的键值类型的定义吧。

typedef enum  //cjson 数据类型

{

    Json_NULL,           //

    Json_Object,         //对象

    Json_Arrary,         //数组 

    Json_String,         //字符串

    Json_Number_Int,     //整形

    Json_Number_Double,  //浮点型

    Json_Bool,           //布尔型

}Json_type;

上面的结构体如果大家不清楚就请好好琢磨琢磨吧!一定要整理清楚才行!!!

/**********************************************************************************************/

/******************************等待大家理清楚结构体逻辑*********************************/

/**********************************************************************************************/

到这里,大家应该都理清楚了结构体的逻辑了,那么接下来我们就正式开始解析吧,首先我们拿到这样一串字符串,我们从第一个字节开始分析,首先作为json字符串的话最外层默认是没有名字的对象,也就是说第一个字符应该是“{”,如果不是那么字符串就是不合法的,直接报错就OJBK了。

接下来我们开始对第一次对象开始解析,对象里面的数据一定是键值对,也就是说一定是有键名和键值,如果没有就可以宣告字符串非法,解析失败了。那么对于一个键值对来说,首先我们应该获取它的键名,键名是由字符串构成的,那么解析键名一定是以“”开始和结束的,键名后面一定是紧紧跟着“:”的,否则就是非法的;那么接下来就可以解析键值了,解析键值的时候可以根据他的第一个字节来判断键值对类型,合法的字符串只可能有一下几种类型

  1. 以“””开始的字符串类型
  2. 以“{”开始的对象类型
  3. 以“[”开始的数组类型
  4. 以“t“或者“f”开始的布尔类型
  5. 以“0”-“9”或者“-”开始的数字类型
  6. 这个字符串又他妈非法了。

那么得到键值类型后就可以把键值解出来了,如果是对象型或者数组型的,就进入child结点解析,直到解析的键值后面跟的是}或者]就可以退出child了,如果解析过程中出现错误或者解析到空字符那就完了,这个字符串又是非法的了,你想想你是以{进去的最后出来不是},这个字符串是不是有问题?

其他的类型各有个的特点,例如数组不可能出现键名,数字不可能出现除了’0’-‘9’’-’、‘.‘以外的字符,并且‘-’只可能出现一个在最前面,‘.’前面必须要有数字,并且只能出现一次等等,根据这些特点可以把所有的键值都给解出来放到对应的变量里面。这里我就不一一说明了。

不过这样说起来是很容易,这中间涉及到动态内存分配与释放,结构体,链表,函数递归,真的做起来的时候还是有点绕人的,大家只要仔细的写应该还是没有问题的,相信大家的C语言能力都比我要厉害,在我写的代码中,用来解析json字符串的函数是:

/*

    函数名:cJson_parse_string_to_object

    传入参数:unsigned char *cJson_string 将被转换的json字符串

    返回参数:cJSON_P: [CJSON_NULL:转换失败,other:转换成功,返回json数据结构句柄]

    函数功能:将json字符串转换成json数据结构

*/

cJSON_P cJson_parse_string_to_object (const unsigned char *cJson_string)//json字符串转换成json数据结构

在我的代码中注释得很详细,大家可以下载下来看看。

  • json数据结构构析成json字符串

解析会了,那么我们构析就很简单了,只需要根据键值类型添加相应的特点字符就行了,例如对象类型的就需要在字符串前后加上{}包括起来,其他的如是,由于篇幅的关系,我就不详细讲解解析过程了,因为解析过程相对简单,只要了解了json的数据结构与json的组包特点,相信大家都很简单能构析出来,在我的代码中,将json数据结构构析成一个json字符串的函数是:

/*

    函数名:cJson_parse_object_to_string

    传入参数:cJSON_P json 将被转换的json数据结构的句柄

    返回参数:unsigned char*: [CJSON_NULL:转换失败,other:转换成功,返回字符串指针]

    函数功能:将json数据结构转换成json字符串

*/

unsigned charcJson_parse_object_to_string (cJSON_P json//cJson数据结构转换成json字符串

 

其实一般来说大家有这两个函数就足够满足工作需求了,不过我考虑到大家可能会有更高的需求,于是我做了一整套我认为会用到的功能,包括解析、构析、打印、删除、增添、修改、构建等功能封装成函数供大家使用,大家有兴趣可以自己研究一下,这些功能我自己都测试过了,大家可以放心的使用,当然也欢迎大家对这份代码进行测试并反馈问题,大家共同进步。

 

/*

    函数名:printf_cJson

    传入参数:cJSON_P json 需要打印的json数据结构地址

    返回参数:Json_err_t: [Json_ERR:数据打印失败,Json_OK:数据打印成功]

    函数功能:根据传入的json数据结构地址将json结构的数据打印出来

*/

Json_err_t printf_cJson (cJSON_P json)  //打印cJson数据结构

 

/*

    函数名:printf_cJson_key_node

    传入参数:cJSON_P json 需要打印的json数据结点

    返回参数:Json_err_t: [Json_ERR:数据打印失败,Json_OK:数据打印成功]

    函数功能:根据传入的json数据结构地址将对应的json结点数据打印出来,仅仅打印单个结点数据

*/

Json_err_t printf_cJson_key_node (cJSON_P json//打印cJson数据结构节点

 

/*

    函数名:cJson_parse_string_to_object

    传入参数:unsigned char *cJson_string 将被转换的json字符串

    返回参数:cJSON_P: [CJSON_NULL:转换失败,other:转换成功,返回json数据结构句柄]

    函数功能:将json字符串转换成json数据结构

*/

cJSON_P cJson_parse_string_to_object (const unsigned char *cJson_string)//json字符串转换成json数据结构

 

/*

    函数名:cJson_parse_object_to_string

    传入参数:cJSON_P json 将被转换的json数据结构的句柄

    返回参数:unsigned char*: [CJSON_NULL:转换失败,other:转换成功,返回字符串指针]

    函数功能:将json数据结构转换成json字符串

*/

unsigned charcJson_parse_object_to_string (cJSON_P json//cJson数据结构转换成json字符串

 

/*

    函数名:cJson_find_keyName_node

    传入参数:cJSON_P json 被查找的json数据结构的句柄

             const unsigned char*keyName 被查找的结点路径名称 格式 object.arrary[0]

    返回参数:cJSON_P: [CJSON_NULL:查找失败,other:查找成功,返回对应结点json句柄]

    函数功能:根据结点路径在指定json数据结构中查找结点,并返回其结点句柄

*/

cJSON_P cJson_find_keyName_node (cJSON_P json,const unsigned char*keyName//根据键值名查找键值对所在的节点

 

/*

    函数名:cJson_delete

    传入参数:cJSON_P 被删除的json数据结构起始结点地址(必须是整个json结构的起始地址)

    返回参数:Json_err_t: [Json_ERR:删除json失败,Json_OK:删除json成功]

    函数功能:删除json数据结构

*/

Json_err_t cJson_delete (cJSON_P json//删除整个数据结构

 

/*

    函数名:cJson_delete_key_node

    传入参数:cJSON_P *json 被删除的json结点的结点地址指针

    返回参数:Json_err_t: [Json_ERR:删除json结点失败,Json_OK:删除json结点成功]

    函数功能:删除json单个结点,如果该结点时object或者arrary类型,则将其下面的child结点一并删除

*/

Json_err_t cJson_delete_key_node (cJSON_P *json//根据节点地址删除对应节点

 

/*

    函数名:cJson_delete_key_node

    传入参数:cJSON_P *json 被删除的json结点所在的json数据结构的句柄指针

             const unsigned char*keyName 要被删除的json结点的路径名称 格式 object.arrary[0]

    返回参数:Json_err_t: [Json_ERR:删除json结点失败,Json_OK:删除json结点成功]

    函数功能:删除json单个结点,如果该结点时object或者arrary类型,则将其下面的child结点一并删除

*/

Json_err_t cJson_delete_node_for_keyName (cJSON_P *json,const unsigned char*keyName//根据键名删除所在节点

 

/*

    函数名:cJson_change_node

    传入参数:cJSON_P json 被修改的json结点地址

             const void* value 将被更新的结点值

             Json_type value_type 被更新的结点值的类型属性

    返回参数:Json_err_t: [Json_ERR:更新json结点数据失败,Json_OK:更新json结点数据成功]

    函数功能:指定json结点更新json结点数据

*/

Json_err_t cJson_change_node   (cJSON_P json,const voidvalue,Json_type value_type//根据结点修改对应值

 

/*

    函数名:cJson_change_keyName_node

    传入参数:cJSON_P json 被修改的json结点的句柄地址

             const unsigned char*keyName 将被更新的结点路径地址 格式 object.arrary[0]

             const void* value 将被更新的结点值

             Json_type value_type 被更新的结点值的类型属性

    返回参数:Json_err_t: [Json_ERR:更新json结点数据失败,Json_OK:更新json结点数据成功]

    函数功能:更新json结点数据

*/

Json_err_t cJson_change_keyName_node (cJSON_P json,const unsigned char*keyName,const voidvalue,Json_type value_type//根据键值名修改对应值

/*

    函数名:cJson_addition_node

    传入参数:cJSON_P json 被新增的json结点的前一个结点

             cJSON_P node 被新增的json结点

    返回参数:Json_err_t: [Json_ERR:新增json结点失败,Json_OK:新增json结点成功]

    函数功能:在指定json结点后面新增node数据结点

*/

Json_err_t cJson_addition_node (cJSON_P json,cJSON_P node//json后添加node

 

/*

    函数名:cJson_addition_node_for_keyName

    传入参数:cJSON_P json 被新增的json结点所在的json句柄

             const unsigned char*keyName  被新增的json结点的前一个结点的路径名称 格式 object.arrary[0]

             cJSON_P node 被新增的json结点

    返回参数:Json_err_t: [Json_ERR:新增json结点失败,Json_OK:新增json结点成功]

    函数功能:在指定json数据结构中的keyName后面新增node数据结点

*/

Json_err_t cJson_addition_node_for_keyName (cJSON_P json,const unsigned char*keyName,cJSON_P node//根据键值名 在后面添加数据

 

/*

    函数名:cJson_build_node

    传入参数:const unsigned char*keyName  被构建的json数据结点的名称

             void* value 被构建的数据结点的值

             Json_type value_type 被构建的json数据结点的值的属性

    返回参数:cJSON_P: [CJSON_NULL:构建json数据结点失败,other:构建的json数据结点成功 返回新构建的数据结点的句柄]

    函数功能:构建一个新的json数据结点

*/

cJSON_P cJson_build_node (const unsigned char*keyName,voidvalue,Json_type value_type//创建一个json数据结构体

          

 

 

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

C语言解析json数据结构分析与教程 的相关文章

随机推荐

  • 设置VLC播放器进行RTSP推流视频

    设置VLC播放器进行RTSP推流视频 一 推流与拉流概念 二 设置推流端 三 设置拉流端 播放端 VLC官网 https www videolan org 一 推流与拉流概念 首先 做几个名词解释 推流 指的是把采集阶段封包好的内容传输到服
  • Easyexcel 3.1.1版本动态表头样式

    需求 动态生成的表头 key value的形式 List
  • Qt中的表格控件QTableWidget是一个非常常用的UI组件

    Qt中的表格控件QTableWidget是一个非常常用的UI组件 它可以用于展示表格数据 并且还有一些便捷的API以及信号槽机制 非常适合初学者入手 本篇文章将会对QTableWidget的使用进行详细介绍 一 如何创建QTableWidg
  • QT 在Window下驱动HID设备

    QT使用HID设备 QT 在Window下驱动HID设备 准备源码 1 下载HID源码 2 解压 拷贝解压目录下的 3 拷贝解压目录下的 4 打开QT建立新工程 把刚准备的window目录的三个文件 5 在QT 配置 pro文件中的SOUR
  • (四)调整PID控制器参数的指南

    一 控制系统设计快速入门和环境 首先确定一下控制任务 快速 精准地控制 必要的稳定性 时域 上升时间 超调等 频域 带宽 阻尼比 然后明白控制系统特点 类积分器 开环稳定性 高度非线性 非最小相位 1 选择一个控制框架 比如说PID 当然也
  • Windows Server2012常见版本

    目录 常见版本 授权方式 常用功能模块介绍 Windows Server 2012这是Windows 8的服务器版本 并且是Windows Server 2008 R2的继任者 该操作系统已经在2012年8月1日完成编译RTM版 并且在20
  • IntelliJ IDEA:新建文件无法识别类型

    参考 https blog csdn net iningwei article details 106115169 总结 File gt Settings gt Editor gt File Types 右侧 Text 然后下面的窗口往下滑
  • C将两个有序的数组合并成一个有序数组

    编写程序 分别输入两个按从小到大排序的数组 a 和 b 将这两个有序数组合并 使合并后的数组仍是从小到大有序的 合并两个有序数组的方法 有两个有序数组a和b 其中数组a的末尾有足够的空间容纳数组b 将数组b容纳到数组a中 创建一个新数组c
  • MyBatis 多表联合查询及优化

    关于优化 对于优化嘛 我这里简单的提几点 大家可以考虑一下 首先 就是对表的设计 在设计表初期 不仅仅要考虑到数据库的规范性 还好考虑到所谓的业务 以及对性能的影响 比如 如果从规范性角度考虑的话 可能就会分多个表 但是如果从性能角度来考虑
  • 如何实现精致扫雷游戏(可扩散可标记)---保姆级教程

    目录 思路 main函数内容 创建 双子 数组 初始化数组 打印棋盘 布置雷 排查雷 初阶 大致过程 如何统计周围雷的个数 判断是否赢得游戏 进阶 可扩散可标记 1 排雷 2 标记 标记要更改的时show数组 呈现给玩家标记信息 3 取消标
  • Bugku-兔年大吉2

    兔年大吉2
  • 逆向基础:32位软件逆向技术

    在编写win32应用程序时 都必须在源码里实现一个WinMain函数 但windows程序的执行并不是从WinMain函数开始的 首先被执行的是启动函数的相关代码 这段代码是由编译器生成的 启动源代码 crt src wincmdln c中
  • 【leveldb】整体架构

    LevelDb本质上是一套存储系统以及在这套存储系统上提供的一些操作接口 为了便于理解整个系统及其处理流程 我们可以从两个不同的角度来看待 LevleDb 静态角度和动态角度 从静态角度 可以假想整个系统正在运行过程中 不断插入删除读取数据
  • 【鼠标事件 MouseEvent】clientX clientY offsetX offsetX pageX screenX screenY

    鼠标事件 MouseEvent 对象 下clientX clientY offsetX offsetX pageX screenX screenY 定义转自 MDNMouseEvent MouseEvent clientX 只读 鼠标指针在
  • C程序设计实现高内聚低耦合

    要做到高内聚低耦合 重点并不是代码的编写 而是整体程序的设计阶段 程序设计时 要先将要实现的功能列出来 然后设计模块 模块设计后 再进行代码实现 要做到高内聚低耦合 设计模块时需要做到 1 各个模块之间的功能必须明确 2 各个功能模块间实现
  • Mysql数据库基础知识总复习

    前言 小亭子正在努力的学习编程 接下来将开启javaEE的学习 分享的文章都是学习的笔记和感悟 如有不妥之处希望大佬们批评指正 同时如果本文对你有帮助的话 烦请点赞关注支持一波 感激不尽 目录 前言 数据库基础知识 数据 数据库 数据库管理
  • 关于访问后端接口报404的问题——全网最详细的404错误详解

    当我们通过前端向后端发起一个请求调用后端接口时 经常会遇到404的问题 网上关于对404问题介绍的一大堆 其实404问题的本质就两点 在介绍404问题之前先温习一个小的知识点 项目访问路径 项目访问路径 就是定位一个项目的路径 可以理解为项
  • spring整合redis缓存,以注解(@Cacheable、@CachePut、@CacheEvict)形式使用

    maven项目中在pom xml中依赖2个jar包 其他的spring的jar包省略
  • Android高级UI之事件传递之1

    点击事件的传递规则 在介绍点击事件的传递规则之前 首先要明白这里分析的对象是MotionEvent 即点击事件 所谓的事件分发 其实就是对MotionEvent事件的分发过程 即当一个MotionEvent产生以后 系统需要把这个事件传递给
  • C语言解析json数据结构分析与教程

    写在前面的话 本文档旨在归纳总结个人的学习经验与成果 记录自己的成长 随便给大家分享自己解决的思路 为在这方面有需要的朋友提供一个参考 本人能力有限水平一般 文中难免会有一些错误 希望大家抱着辩证客观的态度来阅读 有错误还请各位海涵包容并予