JSON|cJSON 介绍以及具体项目编写

一、JSON介绍

JSON(JavaScript Object Notation 即JavaScript对象表示法)是一种轻量级的数据交换格式。采用完全独立于编程语言的文本格式来存储和表示数据。

  • JSON是一种数据交换格式.
  • JSON独立于编程语言(你不必学习JavaScript).
  • JSON表达数据的方式对通用的编程概念都很友好.

本文主要介绍使用C语言解析JSON文件

二、JSON语法规则

数据结构

  1. 对象(Object):使用花括号{}包裹,由键值对组成。键必须是字符串,使用双引号""包裹,键值对之间用逗号,分隔。
  2. 数组(Array):使用方括号[]包裹,值之间用逗号,分隔。

值的类型

  1. 字符串(String):使用双引号""包裹的 Unicode 字符序列。
  2. 数值(Number):整数或浮点数,不使用引号,数字没有长度限制。
  3. 布尔值(Boolean)truefalse,表示"有"或者"没有",不使用引号。
  4. 空值(Null)null,表示没有这项内容,它不是0,0是一个数字,本质上是在计数,不使用引号。
  5. 嵌套对象或数组:可在值中嵌套对象或数组。数组:是由方括号括起来的一组值构成

注意事项

  • 不允许使用单引号。
  • 不允许包含注释。
  • 不允许使用未定义的变量或特殊符号(如NaNInfinity)。
  • 数组和对象可以包含不同类型的值。
  • 使用冒号:来分割名称和值,名称始终在左侧,一定是字符串,值始终在右侧可以是多种类型

三、cJSON

3.1cJSON介绍和获取

首先在GitHub上下载开源的cjson.h以及cjson.c文件

这里给出附件

使用的时候,只需要将这两个文件复制到工程目录,然后包含头文件cJSON.h即可,如下:
#include "cJSON.h"

3.2cJSON数据结构和设计思想

核心数据结构:cJSON 结构体

cJSON 使用单一结构体表示所有 JSON 类型,通过 type 字段区分不同类型。

cJSON.h中源码如下

typedef struct cJSON {struct cJSON *next,*prev;	/* next/prev allow you to walk array/object chains. Alternatively, use GetArraySize/GetArrayItem/GetObjectItem */struct cJSON *child;		/* An array or object item will have a child pointer pointing to a chain of the items in the array/object. */int type;					/* The type of the item, as above. */char *valuestring;			/* The item's string, if type==cJSON_String */int valueint;				/* The item's number, if type==cJSON_Number */double valuedouble;			/* The item's number, if type==cJSON_Number */char *string;				/* The item's name string, if this item is the child of, or is in the list of subitems of an object. */
} cJSON;

统一表示所有 JSON 类型:将其中的一条JSON数据抽象出来,也就是一个键值对,用上面的结构体 strcut cJSON 来表示

  • 通过 type 字段(如 JSON_ObjectJSON_ArrayJSON_Number)区分不同类型。
  • 数值类型通过 valueint 和 valuedouble 存储,字符串类型通过 valuestring 存储。

双向链表管理数组和对象成员

  • 数组(JSON_Array)和对象(JSON_Object)使用双向链表 next 和 prev 连接成员。
  • 对象的键值对通过 child 指针指向第一个成员,每个成员的 string 字段存储键名

递归嵌套结构

  • 子节点可以是任意 JSON 类型,支持无限层级嵌套(如数组包含对象,对象包含数组)。

设计思想

1.轻量级与可移植性
  • 无依赖:仅依赖标准 C 库(stdlib.hstring.hmath.h),无需额外依赖。
  • 单文件实现:核心代码集中在 cJSON.c 和 cJSON.h,便于集成到嵌入式系统或资源受限环境。
2. 简单易用的 API

提供直观的函数接口,例如:

  • cJSON_Parse():解析 JSON 字符串为 cJSON 对象。
  • cJSON_GetObjectItem():通过键名获取对象成员。
  • cJSON_Print():将 cJSON 对象序列化为 JSON 字符串。
  • cJSON_CreateObject()/cJSON_CreateArray():创建 JSON 对象 / 数组。
3. 动态内存管理
  • 使用 malloc() 和 free() 动态分配和释放内存,需手动调用 cJSON_Delete() 释放不再使用的对象。
  • 支持自定义内存分配函数(通过 cJSON_Hooks 结构体),适配不同环境。
4. 错误处理
  • 解析失败时返回 NULL,通过 cJSON_GetErrorPtr() 获取错误位置。
  • 不提供详细错误信息(如行号、具体错误类型),适合快速解析简单 JSON。

3.3cJSON数据封装(含源码分析)

封装JSON数据的过程,其实就是创建链表和向链表中添加节点的过程。

1.创建头指针

 cJSON* cjson_test = NULL;

2.创建头结点,并将头指针指向头结点

cjson_test = cJSON_CreateObject();

3.调用函数向链表中插入结点


cJSON_AddNullToObject(cJSON * const object, const char * const name);cJSON_AddTrueToObject(cJSON * const object, const char * const name);cJSON_AddFalseToObject(cJSON * const object, const char * const name);cJSON_AddBoolToObject(cJSON * const object, const char * const name, const cJSON_bool boolean);cJSON_AddNumberToObject(cJSON * const object, const char * const name, const double number);cJSON_AddStringToObject(cJSON * const object, const char * const name, const char * const string);cJSON_AddRawToObject(cJSON * const object, const char * const name, const char * const raw);cJSON_AddObjectToObject(cJSON * const object, const char * const name);cJSON_AddArrayToObject(cJSON * const object, const char * const name);

输出JSON数据

(char *) cJSON_Print(const cJSON *item);

一段完整的JSON数据就是一条长长的链表,cJSON提供了一个API,可以将整条链表中存放的JSON信息输出到一个字符串中,使用的时候,只需要接收该函数返回的指针地址即可。

3.4cJSON数据解析(含源码分析)

数据解析调用cJSON_Parse(const char *value)

解析JSON数据的过程,其实就是剥离一个一个链表节点(键值对)的过程

// 1.创建链表头指针:
cJSON* cjson_test = NULL;
//2.解析整段JSON数据,并将链表头结点地址返回,赋值给头指针:解析整段数据使用的API只有一个:
(cJSON *) cJSON_Parse(const char *value);
//3.根据键值对的名称从链表中取出对应的值,返回该键值对(链表节点)的地址
(cJSON *) cJSON_GetObjectItem(const cJSON * const object, const char * const string);
//4.如果JSON数据的值是数组,使用下面的两个API提取数据:
(int) cJSON_GetArraySize(const cJSON *array);
(cJSON *) cJSON_GetArrayItem(const cJSON *array, int index);

3.5cJSON内存管理

cJSON的所有操作都是基于链表的,cJSON在使用过程中大量的使用malloc从堆中分配动态内存的,所以在使用完之后,应当及时调用下面的函数,清空cJSON指针所指向的内存,该函数也可用于删除某一条数据:

void cJSON_Delete(cJSON *item);

3.6给出代码示例封装及解析

嵌套的数据封装时递归封装解析时也递归解析

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "cJSON.h"// 封装(生成)JSON 数据
char* encapsulate_json_data() {// 创建根对象cJSON *root = cJSON_CreateObject();// 创建 user 对象cJSON *user = cJSON_CreateObject();cJSON_AddNumberToObject(user, "id", 12345);cJSON_AddStringToObject(user, "name", "Alice");// 创建 contact 对象cJSON *contact = cJSON_CreateObject();cJSON_AddStringToObject(contact, "email", "alice@example.com");// 创建 phone 数组cJSON *phone = cJSON_CreateArray();cJSON_AddItemToArray(phone, cJSON_CreateString("123-456-7890"));cJSON_AddItemToArray(phone, cJSON_CreateString("098-765-4321"));cJSON_AddItemToObject(contact, "phone", phone);// 将 contact 添加到 usercJSON_AddItemToObject(user, "contact", contact);// 将 user 添加到根对象cJSON_AddItemToObject(root, "user", user);// 添加布尔值cJSON_AddBoolToObject(root, "isActive", 1);  // 1 表示 true// 添加嵌套数组cJSON *nested_array = cJSON_CreateArray();cJSON_AddItemToArray(nested_array, cJSON_CreateNumber(100));cJSON_AddItemToArray(nested_array, cJSON_CreateString("nested value"));cJSON *nested_obj = cJSON_CreateObject();cJSON_AddStringToObject(nested_obj, "key", "value");cJSON_AddItemToArray(nested_array, nested_obj);cJSON_AddItemToObject(root, "nested", nested_array);// 生成 JSON 字符串char *json_str = cJSON_Print(root);// 释放 JSON 对象cJSON_Delete(root);return json_str;
}// 解析 JSON 数据
void parse_json_data(const char *json_str) {// 解析 JSON 字符串cJSON *root = cJSON_Parse(json_str);if (root == NULL) {const char *error_ptr = cJSON_GetErrorPtr();if (error_ptr != NULL) {fprintf(stderr, "Error before: %s\n", error_ptr);}return;}// 提取基本字段cJSON *isActive = cJSON_GetObjectItem(root, "isActive");if (cJSON_IsBool(isActive)) {printf("isActive: %s\n", isActive->valueint ? "true" : "false");}// 提取 user 对象cJSON *user = cJSON_GetObjectItem(root, "user");if (cJSON_IsObject(user)) {cJSON *id = cJSON_GetObjectItem(user, "id");cJSON *name = cJSON_GetObjectItem(user, "name");if (cJSON_IsNumber(id) && cJSON_IsString(name)) {printf("User ID: %d\n", id->valueint);printf("User Name: %s\n", name->valuestring);}// 提取 contact 对象cJSON *contact = cJSON_GetObjectItem(user, "contact");if (cJSON_IsObject(contact)) {cJSON *email = cJSON_GetObjectItem(contact, "email");if (cJSON_IsString(email)) {printf("Email: %s\n", email->valuestring);}// 提取 phone 数组cJSON *phone = cJSON_GetObjectItem(contact, "phone");if (cJSON_IsArray(phone)) {int array_size = cJSON_GetArraySize(phone);printf("Phone Numbers (%d):\n", array_size);for (int i = 0; i < array_size; i++) {cJSON *phone_item = cJSON_GetArrayItem(phone, i);if (cJSON_IsString(phone_item)) {printf("  %d: %s\n", i+1, phone_item->valuestring);}}}}}// 解析嵌套数组cJSON *nested = cJSON_GetObjectItem(root, "nested");if (cJSON_IsArray(nested)) {printf("Nested Array:\n");int array_size = cJSON_GetArraySize(nested);for (int i = 0; i < array_size; i++) {cJSON *item = cJSON_GetArrayItem(nested, i);printf("  Item %d: ", i+1);if (cJSON_IsNumber(item)) {printf("Number = %f\n", item->valuedouble);} else if (cJSON_IsString(item)) {printf("String = %s\n", item->valuestring);} else if (cJSON_IsObject(item)) {printf("Object\n");cJSON *key = cJSON_GetObjectItem(item, "key");if (cJSON_IsString(key)) {printf("    key = %s\n", key->valuestring);}}}}// 释放 JSON 对象cJSON_Delete(root);
}int main() {// 封装 JSON 数据char *json_str = encapsulate_json_data();if (json_str == NULL) {fprintf(stderr, "Failed to generate JSON data.\n");return 1;}// 打印生成的 JSONprintf("Generated JSON:\n%s\n\n", json_str);// 解析 JSON 数据printf("Parsing JSON data...\n");parse_json_data(json_str);// 释放 JSON 字符串free(json_str);return 0;
}

四、cJSON项目

这个项目实现了一个使用 C 语言解析 JSON 文件的应用程序,主要内容是从包含中国行政区划信息的 JSON 文件中提取特定省份的城市列表。

具体实现思路

因为cJSON.h中并没有定义对文件的操作,所以通过使用标准 C 库函数 fopenfread 和 fseek 读取 JSON 文件内容,把文件内容读取到字符串中,然后解析字符串,注意使用动态分配内存存储 JSON 数据,并在使用后正确释放,实际操作直接使用 cJSON 库解析 JSON 数据结构,包括对象、数组的遍历,最后通过使用 strcmp 进行字符串比较,即可定位目标省份。

代码:

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "cJSON.h"char* read_file(const char* filename) {FILE* f = fopen(filename, "rb");if (!f) {perror("无法打开文件");return NULL;}fseek(f, 0, SEEK_END);long size = ftell(f);fseek(f, 0, SEEK_SET);char* buff = (char*)malloc(size + 1);if (buff == NULL) {perror("创建内存失败");fclose(f);return NULL;}size_t bytes_read = fread(buff, 1, size, f);//目标缓冲区 buff,每个数据项的大小(这里是 1 字节),要读取的数据项数量 size,以及文件流 f。if (bytes_read < size) {if (feof(f)) {//feof 函数用于检查文件流 f 是否已经到达文件末尾。printf("警告:文件已到达末尾,读取 %zu 字节(期望 %ld 字节)\n", bytes_read, size);}else if (ferror(f)) {perror("读取错误");free(buff);fclose(f);return NULL;}}buff[bytes_read] = '\0';fclose(f);return buff;
}int main() {char* json_str = read_file("city.json");if (json_str == NULL) {return -1;}cJSON* root = cJSON_Parse(json_str);if (root == NULL) {const char* error_ptr = cJSON_GetErrorPtr();if (error_ptr != NULL) {fprintf(stderr, "JSON解析错误: %s\n", error_ptr);}free(json_str);return -1;}// 检查 root 是否为数组if (root->type != cJSON_Array) {printf("JSON 根节点不是数组类型\n");cJSON_Delete(root);free(json_str);return -1;}const char* target_province = "陕西";int province_found = 0;// 遍历所有省份int province_count = cJSON_GetArraySize(root);for (int i = 0; i < province_count; i++) {cJSON* province_item = cJSON_GetArrayItem(root, i);if (!province_item) continue;// 获取省份名称cJSON* name = cJSON_GetObjectItem(province_item, "label");if (!name || name->type != cJSON_String) continue;// 检查是否为目标省份if (strcmp(name->valuestring, target_province) == 0) {printf("找到省份:%s\n", name->valuestring);province_found = 1;// 获取城市数组cJSON* cities_array = cJSON_GetObjectItem(province_item, "children");//注意数据嵌套关系,城市在省份的children数组里if (cities_array && cities_array->type == cJSON_Array) {int city_count = cJSON_GetArraySize(cities_array);printf("包含 %d 个城市:\n", city_count);for (int j = 0; j < city_count; j++) {cJSON* city_item = cJSON_GetArrayItem(cities_array, j);if (!city_item) continue;cJSON* city_name = cJSON_GetObjectItem(city_item, "label");if (city_name && city_name->type == cJSON_String) {printf("  %d. %s\n", j + 1, city_name->valuestring);}}}else {printf("未找到该省份的城市数据\n");}break; // 找到目标省份后退出循环}}if (!province_found) {printf("未找到省份:%s\n", target_province);}cJSON_Delete(root);free(json_str);return 0;
}

运行结果:

这里以查找陕西省的城市为例:

五、 JSON的应用场景有哪些?

多个应用场景:

  1. Web API 数据传输

    • RESTful API 的请求和响应格式
    • 前后端数据交互(如 AJAX 请求)
  2. 配置文件

    • 应用程序配置(如 Node.js 的 package.json)
    • 开发工具配置(如 ESLint、Babel)
  3. 数据存储

    • NoSQL 数据库(如 MongoDB)存储文档
    • 临时数据缓存(如 Redis)
  4. 跨语言数据交换

    • 微服务间通信(如通过消息队列)
    • 不同系统间数据集成
  5. 移动应用开发

    • 与服务器交换数据(如 JSON API)
    • 本地存储(如 React Native 的 AsyncStorage)
  6. 日志和监控系统

    • 结构化日志输出
    • 监控指标传输
  7. 前端框架

    • React/Vue 组件属性传递
    • 路由配置

优势总结:

  • 轻量级,易于阅读和编写
  • 支持嵌套结构,适合复杂数据
  • 几乎所有编程语言都有解析库
  • 与 JavaScript 原生兼容
  • 比 XML 更简洁,解析效率更高

尾语:项目学习并完成于2025.5.10,如果觉得有帮助有收获可以点赞收藏,会持续更新输出有用的内容,感兴趣可以关注我!

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

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

相关文章

【LLaMA-Factory】使用LoRa微调训练DeepSeek-R1-Distill-Qwen-7B

【LLaMA-Factory】使用LoRa微调训练DeepSeek-R1-Distill-Qwen-7B 本地环境说明禁用开源驱动nouveau安装nvidia-smi安装Git环境安装Anaconda(conda)环境下载DeepSeek-R1-Distill-Qwen-7B模型安装LLaMA-Factory下载LLaMA-Factory安装LLaMA-Factory依赖修改环境变量安装deepspeedA…

初始图形学(7)

上一章完成了相机类的实现&#xff0c;对之前所学的内容进行了封装与整理&#xff0c;现在要学习新的内容。 抗锯齿 我们放大之前渲染的图片&#xff0c;往往会发现我们渲染的图像边缘有尖锐的"阶梯"性质。这种阶梯状被称为"锯齿"。当真实的相机拍照时&a…

vllm笔记

目录 vllm简介vllm解决了哪些问题&#xff1f;1. **瓶颈&#xff1a;KV 缓存内存管理低效**2. **瓶颈&#xff1a;并行采样和束搜索中的内存冗余**3. **瓶颈&#xff1a;批处理请求中的内存碎片化** 快速开始安装vllm开始使用离线推理启动 vLLM 服务器 支持的模型文本语言模型生…

访问网站提示“不安全”“有风险”怎么办?

访问网站提示“不安全”“有风险”有以下几种解决方案 一、理解警告类型 1.“不安全”提示&#xff08;HTTP网站&#xff09; 原因&#xff1a;网站未使用HTTPS加密&#xff0c;传输数据&#xff08;如密码、支付信息&#xff09;可能被窃取。 表现&#xff1a;浏览器地址栏显…

vue3的响应式设计原理

Vue 3 的响应式设计是其核心特性之一&#xff0c;依赖于 Proxy 和 依赖收集机制&#xff0c;相比 Vue 2 的 Object.defineProperty&#xff0c;Vue 3 的响应式系统更加高效、灵活且易于维护。 以下是 Vue 3 响应式设计的核心原理&#xff1a; 一、核心机制概览 使用 Proxy 实现…

C++模板笔记

Cpp模板笔记 文章目录 Cpp模板笔记1. 为什么要定义模板2. 模板的定义2.1 函数模板2.1.1 函数模板的重载2.1.2 头文件与实现文件形式&#xff08;重要&#xff09;2.1.3 模板的特化2.1.4 模板的参数类型2.1.5 成员函数模板2.1.6 使用模板的规则 2.2 类模板2.3 可变参数模板 模板…

递归函数(斐波那契数列0,1,1,2,3,5,8,13,21,34,55...)

目录 一、斐波那契数列&#xff08;兔子问题&#xff09; 二、迭代法&#xff08;用while循环推下一项 ) 三、递归函数 (函数的定义中调用函数自身的一种函数定义方式) 四、递归函数的底层逻辑推理 (二叉树推倒最左下节点回退法) 一、斐波那契数列&#xff08;兔子问题&…

光的本质(以暗物质维度粒子为介质的能量传导)

一、光的概要描述 1、光的本质是能量传导 空间中均匀分布着暗物质维度粒子。光不是粒子也不是波,而是没有质量和形态的能量,在临近暗物质粒子之间的一种能量传递。 2、光能传递类似牛顿钟摆(空间中的牛顿钟摆) 当光能能量骚动一个暗物质粒子后,该暗物质粒…

Open CASCADE学习|管道壳体生成

一、引言 在计算机辅助设计&#xff08;CAD&#xff09;和计算机图形学领域&#xff0c;OpenCASCADE 是一款功能强大的开源 3D 建模库。它提供了丰富的几何和拓扑建模工具&#xff0c;其中管道壳体&#xff08;Pipe Shell&#xff09;生成是其重要功能之一。管道壳体广泛应用于…

JS正则表达式介绍(JavaScript正则表达式)

文章目录 JavaScript正则表达式完全指南正则表达式基础元字符与特殊字符基本元字符. - 点号\d - 数字\D - 非数字\w - 单词字符\W - 非单词字符\s - 空白字符\S - 非空白字符 正则表达式标志常用标志详解g - 全局匹配i - 忽略大小写m - 多行匹配s - 点号匹配所有字符u - Unicod…

Kubernetes 使用 containerd 实现 GPU 支持及 GPU Operator 部署指南

目录 Kubernetes 使用 containerd 实现 GPU 支持及 GPU Operator 部署指南 一、为什么 containerd 是趋势&#xff1f; 二、目标 三、前提条件 四、方式一&#xff1a;containerd nvidia-container-toolkit&#xff08;基础方式&#xff09; 1️⃣ 安装 NVIDIA Containe…

leetcode 2918. 数组的最小相等和 中等

给你两个由正整数和 0 组成的数组 nums1 和 nums2 。 你必须将两个数组中的 所有 0 替换为 严格 正整数&#xff0c;并且满足两个数组中所有元素的和 相等 。 返回 最小 相等和 &#xff0c;如果无法使两数组相等&#xff0c;则返回 -1 。 示例 1&#xff1a; 输入&#xf…

猿人学第十二题-js入门

1. 链接 https://match.yuanrenxue.cn/match/12 2. 抓包分析 2.1. m参数 通过观察&#xff0c;只有m参数要解决&#xff1a; 3. 逆向分析 3.1. 跟栈 直接跟栈吧&#xff0c;一下就出结果了&#xff1a; 可以看到m其实很简单&#xff0c;就是固定字符串 当前页数&#xf…

双系统电脑中如何把ubuntu装进外接移动固态硬盘

电脑&#xff1a;win11 ubuntu22.04 实体机 虚拟机&#xff1a;VMware17 镜像文件&#xff1a;ubuntu-22.04.4-desktop-amd64.iso 或者 ubuntu20.4的镜像 外接固态硬盘1个 一、首先win11中安装vmware17 具体安装方法&#xff0c;网上很多教程 二、磁盘分区 1.在笔…

202535| Kafka架构与重要概念+幂等性+事务

好的&#xff01;以下是关于 Kafka 架构 以及其 重要概念 的详细介绍&#xff0c;结合 Mermaid 图形 和 表格&#xff0c;帮助你更好地理解各个概念的关系和作用。 Kafka 架构与重要概念 Kafka 是一个分布式消息系统&#xff0c;广泛应用于日志收集、流处理、事件驱动架构等场…

从0开始学习大模型--Day05--理解prompt工程

提示词工程原理 N-gram&#xff1a;通过统计&#xff0c;计算N个词共同出现的概率&#xff0c;从而预测下一个词是什么。 深度学习模型&#xff1a;有多层神经网络组成&#xff0c;可以自动从数据中学习特征&#xff0c;让模型通过不断地自我学习不断成长&#xff0c;直到模型…

Amazing晶焱科技:系统级 EOS 测试方法 - System Level EOS Testing Method

系统上常见的EOS测试端口以AC电源、电话线&#xff08;RJ11&#xff09;、同轴电缆&#xff08;coaxial cable&#xff09;以及以太网络&#xff08;RJ45&#xff09;最常见&#xff0c;这些端口因有机会布线至户外的关系&#xff0c;受到EOS/Surge冲击的几率也大大提升。因此电…

数据结构—(概述)

目录 一 数据结构&#xff0c;相关概念 1. 数据结构&#xff1a; 2. 数据(Data): 3. 数据元素(Data Element): 4. 数据项&#xff1a; 5. 数据对象(Data Object): 6. 容器&#xff08;container&#xff09;&#xff1a; 7. 结点&#xff08;Node&#xff09;&#xff…

Vue 两种导航方式

目录 一、声明式导航 二、编程式导航 三、两句话总结 一、声明式导航 1. 传参跳转&#xff1a; <router-link :to"/user?nameCHEEMS&id114514">Query传参 </router-link><router-link :to"/user?参数名1参数值1&参数名2参数值2&a…

QTableWidget实现多级表头、表头冻结效果

最终效果&#xff1a; 实现思路&#xff1a;如果只用一个表格的话写起来比较麻烦&#xff0c;可以考虑使用两个QTableWidget组合&#xff0c;把复杂的表头一个用QTableWidget显示&#xff0c;其他内容用另一个QTableWidget。 #include "mainwindow.h" #include &qu…