QT开发---网络编程下
HTTP协议
HTTP(HyperText Transfer Protocol,超文本传输协议)是互联网上应用最为广泛的协议之一,用于客户端和服务器之间的通信。默认端口80,传输层使用的是TCP协议
特点
无连接:HTTP协议是无连接的,这意味着每次请求 - 响应完成后,连接就会关闭。客户端和服务器之间不会保持持久连接。
无状态:HTTP协议是无状态的,服务器不会保存客户端请求之间的状态信息。每次请求都是独立的,服务器不会记住之前客户端的请求内容,除非通过其他机制(如Cookie)来实现状态管理。
协议格式:
HTTP 请求
请求行:包含请求方法、请求URI和HTTP协议版本。常见的请求方法有:
GET:用于请求服务器返回指定资源的内容。例如,当你在浏览器中输入网址并回车时,浏览器会发送一个GET请求到服务器,请求该网址对应的网页内容。
POST:用于向服务器提交数据,通常用于表单提交。例如,当你在登录页面输入用户名和密码并点击“登录”按钮时,浏览器会发送一个POST请求,将用户名和密码数据提交给服务器。
PUT:用于向服务器上传整个资源,通常用于更新资源。
DELETE:用于请求服务器删除指定的资源。
HEAD:与GET方法类似,但它只返回响应头,不返回响应体内容,常用于检查资源是否存在。
OPTIONS:用于获取服务器支持的HTTP方法。
CONNECT:用于建立隧道,常用于代理服务器。
TRACE:用于回显请求的内容,主要用于调试。
请求头:包含客户端向服务器发送的附加信息,例如:
Host:指定请求的主机名和端口号。
User-Agent:标识客户端的类型(如浏览器版本、操作系统等)。
Accept:告诉服务器客户端可以接受的内容类型(如HTML、JSON等)。
Content-Type:用于POST或PUT请求,指定请求体的媒体类型(如
application/json
、application/x-www-form-urlencoded
等)。Cookie:用于在客户端和服务器之间传递状态信息。
空行:表示请求头部的结束。
请求体:对于POST、PUT等方法,请求体中包含要提交给服务器的数据,如表单数据或JSON格式的数据。
HTTP 响应
状态行:包含HTTP协议版本、状态码和状态消息。状态码是一个三位数字,用于表示请求的结果:
1xx:信息性状态码,表示请求已被接收,继续处理。例如,
100 Continue
表示客户端应继续发送请求的其余部分。2xx:成功状态码,表示请求已成功处理。例如,
200 OK
表示请求正常处理成功;201 Created
表示请求成功并且服务器创建了新的资源。3xx:重定向状态码,表示需要进一步的操作以完成请求。例如,
301 Moved Permanently
表示请求的资源已被永久移动到新的URL;302 Found
表示请求的资源临时移动到新的URL。4xx:客户端错误状态码,表示客户端发送的请求有语法错误或无法完成请求。例如,
400 Bad Request
表示请求语法有误;401 Unauthorized
表示请求未授权;404 Not Found
表示请求的资源不存在。5xx:服务器错误状态码,表示服务器在处理请求的过程中发生了错误。例如,
500 Internal Server Error
表示服务器内部错误;503 Service Unavailable
表示服务器暂时无法处理请求。
响应头:包含服务器向客户端发送的附加信息,例如:
Content-Type:指定响应体的媒体类型(如
text/html
、application/json
等)。Content-Length:指定响应体的长度(以字节为单位)。
Set-Cookie:用于设置Cookie,将状态信息发送到客户端。
Location:用于重定向,指定新的资源位置。
空行:表示请求头部的结束。
响应体:包含服务器返回给客户端的资源内容,如网页HTML代码、图片数据等。
URL(统一资源定位符)
URI:统一资源标识符,用于唯一标识资源,是一个更广泛的术语。
URL:统一资源定位符,是URI的一个子集,用于标识资源的具体位置,可以直接用于访问资源
结构 scheme://authority/path[?query][#fragment]
fragment:片段标识符,通常用于标识资源内部的某个部分。
query:查询参数,用于向服务器传递额外的信息。
path:资源的路径,通常是一个层次化的路径结构。
authority:资源所在的主机名或IP地址,可选地包括端口号。
scheme:协议类型,如
http
、https
、ftp
等。eg: http://127.0.0.1:8080/?wd=%E4%B8%AD%E5%9B%BD
URL编码通常也被称为百分号编码(URL Encoding,also known as percent-encoding) ,是因为它的编码方式非常简单,使用%百分号加上两位的字符代表一个字节的十六进制形式 。
在 URI 中,某些字符具有特殊含义:
/
表示路径分隔符?
表示查询字符串的开始&
用于分隔查询参数=
用于分隔参数名和参数值URN:统一资源名称,也是URI的一种形式,用于标识资源的名称,而不是位置。
HTTPS(HTTP Secure)
HTTPS是HTTP的安全版本,通过在HTTP协议的基础上添加SSL/TLS(Secure Sockets Layer/Transport Layer Security)加密层来实现安全通信。它能够防止数据在传输过程中被窃听、篡改或伪造。默认端口为443。
HTTPS的实现过程包括证书的颁发和验证。服务器需要向证书颁发机构(CA)申请SSL证书,客户端在与服务器建立HTTPS连接时会验证服务器的证书是否有效。如果证书无效(如证书过期、证书颁发机构不可信等),浏览器会提示用户连接不安全。
HTTP / 网络请求类
QNetworkAccessManager类
基于事件驱动模型,通过信号和槽机制处理异步网络操作,不会阻塞主线程
Network Access
API是围绕一个QNetworkAccessManager对象构建的,该对象为它发送的请求保存通用配置和设置。它包含代理和缓存配置,以及与这些问题相关的信号,以及可用于监视网络操作进度的应答信号。对于整个Qt应用程序,一个QNetworkAccessManager实例应该足够了。因为QNetworkAccessManager是基于QObject的,所以它只能在它所属的线程中使用。一旦创建了QNetworkAccessManager对象,应用程序就可以使用它通过网络发送请求。提供了一组标准函数,它们接受一个请求和可选数据,每个函数返回一个QNetworkReply对象。返回的对象用于获取响应相应请求而返回的任何数据。
QNetworkAccessManager有一个异步API。当调用上面的replyFinished槽时,它接受的参数是QNetworkReply对象,该对象包含下载的数据以及元数据(报头等)。
注意:请求完成后,用户有责任在适当的时候删除QNetworkReply对象。不要直接在连接finished()的槽内删除。您可以使用deleteLater()函数。
注意:QNetworkAccessManager对它接收到的请求进行排队。并行执行的请求数量取决于协议。目前,对于桌面平台上的HTTP协议,一个主机/端口组合并行执行6个请求。
QNetworkAccessManager
与以下类配合使用:
QNetworkRequest
:封装请求信息(URL、请求头、超时等)QNetworkReply
:封装响应结果(返回数据、状态码、错误信息等)QUrl
:处理 URL 解析QByteArray
:存储请求 / 响应数据
常用方法:
发送请求
get(const QNetworkRequest &request):发送 GET 请求
post(const QNetworkRequest &request, const QByteArray &data):发送 POST 请求(表单数据)
post(const QNetworkRequest &request, QIODevice *data):发送 POST 请求(大文件 / 流数据)
put()/deleteResource():对应 HTTP 的 PUT 和 DELETE 方法配置网络
setProxy(const QNetworkProxy &proxy):设置代理
setCookieJar(QNetworkCookieJar *jar):设置 Cookie 容器
setCache(QNetworkCache *cache):设置缓存信号与槽
finished(QNetworkReply *):请求完成时触发(成功或失败)
downloadProgress(qint64 bytesReceived, qint64 bytesTotal):下载进度更新
uploadProgress(qint64 bytesSent, qint64 bytesTotal):上传进度更新
sslErrors(QNetworkReply *, const QList<QSslError> &):SSL 证书错误时触发
注意事项
- 异步处理:所有请求都是异步的,需通过
finished
信号处理结果,避免阻塞 UI - 资源释放:
QNetworkReply
对象必须通过deleteLater()
释放,否则会导致内存泄漏 - HTTPS 支持:需要在项目文件(
.pro
)中添加QT += network
,并确保系统支持 SSL(可能需要额外的库,如 OpenSSL) - 超时设置:Qt 没有直接设置超时的方法,可通过
QTimer
实现 - 大文件下载:应通过
readyRead
信号分块读取数据,避免一次性加载到内存
QNetworkRequest类
QNetworkRequest is part of the Network Access API and is the class holding the information necessary to send a request over the network. It contains a URL and some ancillary information that can be used to modify the request.
QNetworkRequest是Network Access API的一部分,它是包含通过网络发送请求所需信息的类。它包含一个URL和一些可用于修改请求的辅助信息。
主要功能与用途
- 封装请求 URL:明确网络请求的目标地址,支持 HTTP、HTTPS、FTP 等多种协议。
- 设置请求头:可以添加各种 HTTP 头信息,如
User-Agent
(用于标识客户端身份)、Content-Type
(指定请求体的类型,如表单数据、JSON 数据等)、Authorization
(用于身份验证,如 Token 认证) 等。 - 管理缓存策略:控制请求的缓存行为,例如设置是否从缓存中读取数据,以及是否将响应数据缓存起来。
- 处理重定向:可以配置对重定向的处理方式,决定是否自动跟随重定向请求
常用方法:
常用构造函数
QNetworkRequest():默认构造函数,创建一个空的请求对象
QNetworkRequest(const QUrl &url):根据给定的 QUrl 创建一个请求对象
将请求的目标地址设置为该 QUrl设置 URL
setUrl(const QUrl &url):设置请求的目标 URL
url() const:获取请求的目标 URL设置请求头
setHeader(QNetworkRequest::KnownHeaders header, const QVariant &value):
设置已知类型的请求头,KnownHeaders 是一个枚举类型,包含了常见的 HTTP 头,
比如 ContentTypeHeader(内容类型头)、UserAgentHeader(用户代理头) 等。
setRawHeader(const QByteArray &name, const QByteArray &value):设置自定义的原始请求头
header(QNetworkRequest::KnownHeaders header) const:获取已知类型的请求头的值
rawHeader(const QByteArray &name) const:获取自定义的原始请求头的值缓存相关
setCacheMetaData(const QNetworkCacheMetaData &metaData):设置请求的缓存元数据
cacheMetaData() const:获取请求的缓存元数据
setCachePolicy(QNetworkRequest::CachePolicy cachePolicy):
设置请求的缓存策略
常见的缓存策略包括 NoCache(不使用缓存)、PreferCache(优先使用缓存)等
cachePolicy() const:获取请求的缓存策略重定向相关
setRedirectPolicy(QNetworkRequest::RedirectPolicy redirectPolicy):
设置重定向策略
例如 ManualRedirectPolicy(手动处理重定向)、AutoRedirectPolicy(自动跟随重定向)
redirectPolicy() const:获取请求的重定向策略
QNetworkReply类
QNetworkReply类包含与QNetworkAccessManager发布的请求相关的数据和元数据。与QNetworkRequest一样,它包含一个URL和报头(包括解析格式和原始格式)、一些关于应答状态的信息和应答本身的内容。
QNetworkReply是一个顺序访问的QIODevice,这意味着一旦从对象中读取数据,它就不再被设备保存。因此,如果需要,应用程序有责任保留这些数据。每当从网络接收并处理更多数据时,就会发出readyRead()信号。
当接收到数据时,也会发出downloadProgress()信号,但是如果对内容进行了任何转换(例如,解压缩和删除协议开销),则其中包含的字节数可能不代表接收到的实际字节数。
尽管QNetworkReply是连接到应答内容的QIODevice,但它也会发出uploadProgress()信号,该信号指示具有此类内容的操作的上传进度。
注意:不要删除errorOccurred()或finished()信号所连接槽位中的对象。使用deleteLater()。
主要功能与特性
- 封装服务器返回的响应数据(文本、二进制等)
- 提供请求的状态信息(成功 / 失败、HTTP 状态码等)
- 支持获取响应头、Cookie 等元数据
- 提供上传 / 下载进度通知
- 支持中断请求、处理重定向等操作
常用方法:
获取响应数据
readAll():读取所有响应数据(QByteArray 类型),适合处理小数据
read(qint64 maxSize):读取指定大小的数据,适合大文件分块处理
readyRead() 信号:有新数据可读时触发(用于异步分块读取)状态与错误处理
error():返回错误类型(QNetworkReply::NetworkError 枚举),NoError 表示成功
errorString():返回错误的描述信息(字符串)
isFinished():判断请求是否已完成
isRunning():判断请求是否正在进行HTTP 响应信息
attribute(QNetworkRequest::Attribute code):获取响应属性
如 HTTP 状态码:
常见状态码:200(成功)、404(未找到)、500(服务器错误)等
rawHeaderList():获取所有响应头的名称列表
rawHeader(const QByteArray &headerName):获取指定响应头的值(如 Content-Type)控制请求
abort():中断当前请求
ignoreSslErrors():忽略 SSL 证书错误(谨慎使用,可能有安全风险)信号与槽
finished():请求完成时触发(无论成功或失败)
readyRead():有新数据可读时触发(用于分块读取)
downloadProgress(qint64 bytesReceived, qint64 bytesTotal)
下载进度更新(bytesTotal 为 -1 表示未知大小)
uploadProgress(qint64 bytesSent, qint64 bytesTotal):上传进度更新
sslErrors(const QList<QSslError> &errors):SSL 证书验证失败时触发
成果展示:
#include "mainwindow.h"
#include "./ui_mainwindow.h"
#include <QDebug>
#include <QNetworkRequest>
#include <QUrl>
#include <QNetworkReply>
MainWindow::MainWindow(QWidget *parent): QMainWindow(parent), ui(new Ui::MainWindow)
{ui->setupUi(this);networkAccessManger = new QNetworkAccessManager(this);
}MainWindow::~MainWindow()
{delete ui;
}void MainWindow::on_GET_clicked()
{QUrl url("http://127.0.0.1:8080/work/worktask/work.json");QNetworkRequest request(url);//发送GET请求QNetworkReply *reply = networkAccessManger->get(request);//连接响应完成信号connect(reply, &QNetworkReply::finished, this, [=](){if(reply->error() == QNetworkReply::NoError){qDebug() << "success GET";}else{//处理错误qDebug() << "Error:" << reply->errorString();}reply->deleteLater(); //释放资源});connect(reply, &QNetworkReply::readyRead, this, [=](){//处理成功响应QByteArray data = reply->readAll();qDebug() << "GETdata:\n" << data;});
}void MainWindow::on_POST_clicked()
{QByteArray data;//数据以json格式传输/** json数据格式* {* "username":"xiaowang",* "password":"123456",* }*///QString paramsdata("{\"username\":\"xiaowang\",\"pasword\":\"123456\"}");QString paramsdata("username=xiaowang&pasword=123456");data = paramsdata.toUtf8();QUrl url;url.setScheme("http");url.setHost("127.0.0.1");url.setPort(8080);url.setPath("/work/worktask/work.json");QNetworkRequest request(url);//设置请求头//json格式request.setRawHeader("content-type","application/json");//表单格式//request.setRawHeader("Content-Type", "application/x-www-form-urlencoded");QNetworkReply *reply = networkAccessManger->post(request,data);//连接完成信号//连接响应完成信号connect(reply, &QNetworkReply::finished, this, [=](){if(reply->error() == QNetworkReply::NoError){qDebug() << "success GET";}else{//处理错误qDebug() << "Error:" << reply->errorString();}reply->deleteLater(); //释放资源});connect(reply, &QNetworkReply::readyRead, this, [=](){//处理成功响应QByteArray data = reply->readAll();qDebug() << "GETdata:\n" << data;});
}
利用HFS搭建一个简易服务器进行测试
JSON格式
JSON格式基础概述
JSON 数据由键值对组成,遵循以下规则:
数据由键值对组成:
键(Key)必须是字符串类型,且必须用双引号(
"
)括起来值(Value)可以是以下类型之一:
字符串(用双引号括起来)
数字(整数或浮点数)
布尔值(
true
或false
)数组(用方括号
[]
括起来)对象(用大括号
{}
括起来)null
(表示空值)
键值对之间用逗号(
,
)分隔对象用大括号(
{}
)括起来数组用方括号(
[]
)括起来
JSON 数据可以分为两种主要结构:
对象(Object):
用大括号
{}
括起来,包含一系列键值对键是字符串,值可以是任何合法的 JSON 数据类型
数组(Array):
用方括号
[]
括起来,包含一系列值数组中的值可以是任何合法的 JSON 数据类型
eg:
{"name": "张三","age": 25,"isStudent": false,"address": {"street": "北京市海淀区中关村大街1号","city": "北京","postalCode": "100080"},"hobbies": ["阅读", "编程", "旅行"] }
["苹果","香蕉","橙子" ]
JSON 的优点
轻量级:JSON 数据格式简洁,易于传输
易于阅读和编写:JSON 数据格式接近人类可读的文本格式
语言无关:虽然 JSON 的语法来源于 JavaScript,但它独立于语言,可以被多种编程语言解析和生成
易于解析:大多数编程语言都提供了内置的 JSON 解析和生成工具
Qt中解析JSON数据
在 Qt 中解析 JSON 数据可以使用 Qt 自带的 JSON 模块,该模块提供了许多类来处理 JSON 数据。
JsonDocument
fromJson(const QByteArray &data, QJsonParseError *error = nullptr) const
将 JSON 格式的字节数组解析为QJsonDocument对象,error参数用于接收解析错误信息
toJson(JsonFormat format = Indented)
将QJsonDocument转换为 JSON 格式的字节数组
format可指定缩进(Indented)或紧凑(Compact)格式QJsonObject
contains(const QString &key):判断是否包含指定键
value(const QString &key):获取指定键对应的QJsonValue
keys():返回所有键的列表
toVariantMap():将 JSON 对象转换为QVariantMap(便于快速访问)QJsonArray
size():返回数组元素数量
at(int i):获取索引i处的QJsonValue
foreach 遍历:循环访问数组元素
toVariantList():转换为QVariantListQJsonValue
类型判断:isString()、isDouble()、isBool()、isObject()、isArray()、isNull()
类型转换:toString()、toDouble()、toBool()、toObject()、toArray()从文件读取 JSON(QFile + 异步文件操作)
QFile本身是同步的,但可结合QFileSystemWatcher监控文件变化,触发解析:
QFileSystemWatcher::fileChanged(const QString &path)
当监控的文件内容变化时触发,此时可重新读取并解析 JSON从网络请求获取 JSON(QNetworkAccessManager)
网络请求是典型的异步场景,通过信号触发 JSON 解析:
QNetworkReply::finished()
网络请求完成时触发,此时可读取响应数据并解析
解析HTTP响应后获取的JSON文件
#include "mainwindow.h"
#include "./ui_mainwindow.h"
#include <QDebug>
#include <QNetworkRequest>
#include <QUrl>
#include <QNetworkReply>
#include <QFile>
#include <QIODevice>
#include <QFileInfo>
#include <QDir>
#include <QTextStream>
#include <QJsonParseError>
#include <QJsonObject>
#include <QJsonArray>
MainWindow::MainWindow(QWidget *parent): QMainWindow(parent), ui(new Ui::MainWindow)
{ui->setupUi(this);networkAccessManger = new QNetworkAccessManager(this);
}MainWindow::~MainWindow()
{delete ui;
}void MainWindow::on_GET_clicked()
{QUrl url("http://127.0.0.1:8080/work/worktask/work.json");QNetworkRequest request(url);//发送GET请求QNetworkReply *reply = networkAccessManger->get(request);//连接响应完成信号connect(reply, &QNetworkReply::finished, this, [=](){if(reply->error() == QNetworkReply::NoError){qDebug() << "success GET";}else{//处理错误qDebug() << "Error:" << reply->errorString();}reply->deleteLater(); //释放资源});connect(reply, &QNetworkReply::readyRead, this, [=](){//处理成功响应---将响应数据写入data.json文件QFile file("D:/QT/QTproject/networkHTTP/data.json");//打开文件 WriteOnly模式,若文件存在则覆盖,不存在则创建if (!file.open(QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate)){qDebug() << "文件打开失败:" << file.errorString();return;}QByteArray data = reply->readAll();qDebug() << "GETdata:\n" << data;//写入文件QTextStream out(&file);//设置编码(避免中文乱码)out.setEncoding(QStringConverter::Utf8);//写入内容out << data;//验证文件是否写入成功if (file.error() != QFile::NoError){qDebug() << "文件写入失败:" << file.errorString();return;}file.flush();file.close();});
}void MainWindow::on_POST_clicked()
{QByteArray data;//数据以json格式传输/** json数据格式* {* "username":"xiaowang",* "password":"123456",* }*///QString paramsdata("{\"username\":\"xiaowang\",\"pasword\":\"123456\"}");QString paramsdata("username=xiaowang&pasword=123456");data = paramsdata.toUtf8();QUrl url;url.setScheme("http");url.setHost("127.0.0.1");url.setPort(8080);url.setPath("/work/worktask/work.json");QNetworkRequest request(url);//设置请求头//json格式request.setRawHeader("content-type","application/json");//表单格式//request.setRawHeader("Content-Type", "application/x-www-form-urlencoded");QNetworkReply *reply = networkAccessManger->post(request,data);//连接完成信号//连接响应完成信号connect(reply, &QNetworkReply::finished, this, [=](){if(reply->error() == QNetworkReply::NoError){qDebug() << "success GET";}else{//处理错误qDebug() << "Error:" << reply->errorString();}reply->deleteLater(); //释放资源});connect(reply, &QNetworkReply::readyRead, this, [=](){//处理成功响应QByteArray data = reply->readAll();qDebug() << "GETdata:\n" << data;});
}void MainWindow::on_GET_JSON_clicked()
{//检查文件是否存在QFileInfo fileInfo("D:/QT/QTproject/networkHTTP/data.json");if(!fileInfo.exists()){qDebug() << "文件不存在";return;}//打开JSON文件QFile file("D:/QT/QTproject/networkHTTP/data.json");QByteArray data;//打开文件if(!file.open(QIODevice::ReadOnly | QIODevice::Text)){qDebug() << "文件打开失败:" << file.errorString();return;}data = file.readAll();file.close();//创建QJsonDocumentQJsonParseError parseError;QJsonDocument doc = QJsonDocument::fromJson(data, &parseError);//检查文档有效性if(parseError.error != QJsonParseError::NoError){qDebug() << "解析错误:" << parseError.errorString();return;}//检查JSON文件对象类型---解析JSON文件/*if(doc.isObject()){QJsonObject JsonObj = doc.object();//解析字符串if(JsonObj.contains("name") && JsonObj["name"].isString()){qDebug() << "名称:" << JsonObj["name"].toString();}//解析数字if(JsonObj.contains("version") && JsonObj["version"].isDouble()){qDebug() << "版本:" << JsonObj["version"].toDouble();}//解析布尔值if(JsonObj.contains("enabled") && JsonObj["enabled"].isBool()){qDebug() << "是否启用:" << (JsonObj["enabled"].toBool() ? "是" : "否");}//解析嵌套对象if(JsonObj.contains("config") && JsonObj["config"].isObject()){QJsonObject configObj = JsonObj["config"].toObject();if(configObj.contains("timeout") && configObj["timeout"].isDouble()){qDebug() << "超时设置:" << configObj["timeout"].toInt() << "秒";}}//解析数组if(JsonObj.contains("items") && JsonObj["items"].isArray()){QJsonArray itemsArray = JsonObj["items"].toArray();qDebug() << "数组包含" << itemsArray.size() << "个元素:";for(int i = 0; i < itemsArray.size(); i++){QJsonValue item = itemsArray[i];if(item.isString()){qDebug() << "元素" << i << ":" << item.toString();}}}}//解析JSON数组else if(doc.isArray()){qDebug() << "开始解析JSON数组";QJsonArray JsonArray = doc.array();// 遍历数组(根据实际JSON结构修改)qDebug() << "数组长度:" << JsonArray.size();for(int i = 0; i < JsonArray.size(); i++){QJsonValue element = JsonArray[i];// 假设数组元素是对象if(element.isObject()){QJsonObject obj = element.toObject();if(obj.contains("id") && obj["id"].isDouble()){qDebug() << "第" << i << "个元素ID:" << obj["id"].toInt();}if(obj.contains("name") && obj["name"].isString()){qDebug() << "第" << i << "个元素名称:" << obj["name"].toString();}}}}*///根节点是对象,包含 "user" 字段if(doc.isObject()){QJsonObject rootObj = doc.object();// 解析 user 对象if(rootObj.contains("user") && rootObj["user"].isObject()){QJsonObject userObj = rootObj["user"].toObject();// 解析用户基本信息if(userObj.contains("id") && userObj["id"].isDouble()){qDebug() << "用户ID:" << userObj["id"].toInt();}if(userObj.contains("name") && userObj["name"].isString()){qDebug() << "用户姓名:" << userObj["name"].toString();}if(userObj.contains("email") && userObj["email"].isString()){qDebug() << "用户邮箱:" << userObj["email"].toString();}if(userObj.contains("phone") && userObj["phone"].isString()){qDebug() << "用户电话:" << userObj["phone"].toString();}// 解析地址(嵌套对象)if(userObj.contains("address") && userObj["address"].isObject()){QJsonObject addressObj = userObj["address"].toObject();qDebug() << "用户地址:";if(addressObj.contains("street") && addressObj["street"].isString()){qDebug() << " 街道:" << addressObj["street"].toString();}if(addressObj.contains("city") && addressObj["city"].isString()){qDebug() << " 城市:" << addressObj["city"].toString();}if(addressObj.contains("postalCode") && addressObj["postalCode"].isString()){qDebug() << " 邮编:" << addressObj["postalCode"].toString();}if(addressObj.contains("country") && addressObj["country"].isString()){qDebug() << " 国家:" << addressObj["country"].toString();}}// 解析订单列表(数组)if(userObj.contains("orders") && userObj["orders"].isArray()){QJsonArray ordersArray = userObj["orders"].toArray();qDebug() << "\n订单总数:" << ordersArray.size();for(int i = 0; i < ordersArray.size(); i++){QJsonValue orderVal = ordersArray[i];if(orderVal.isObject()){QJsonObject orderObj = orderVal.toObject();qDebug() << "\n订单" << i+1 << ":";if(orderObj.contains("orderId") && orderObj["orderId"].isString()){qDebug() << " 订单ID:" << orderObj["orderId"].toString();}if(orderObj.contains("orderDate") && orderObj["orderDate"].isString()){qDebug() << " 订单日期:" << orderObj["orderDate"].toString();}if(orderObj.contains("totalAmount") && orderObj["totalAmount"].isDouble()){qDebug() << " 订单总金额:" << orderObj["totalAmount"].toDouble() << "元";}// 解析订单项(数组)if(orderObj.contains("items") && orderObj["items"].isArray()){QJsonArray itemsArray = orderObj["items"].toArray();qDebug() << " 订单项数量:" << itemsArray.size();for(int j = 0; j < itemsArray.size(); j++){QJsonValue itemVal = itemsArray[j];if(itemVal.isObject()){QJsonObject itemObj = itemVal.toObject();qDebug() << " 商品" << j+1 << ":";if(itemObj.contains("productId") && itemObj["productId"].isString()){qDebug() << " 商品ID:" << itemObj["productId"].toString();}if(itemObj.contains("productName") && itemObj["productName"].isString()){qDebug() << " 商品名称:" << itemObj["productName"].toString();}if(itemObj.contains("quantity") && itemObj["quantity"].isDouble()){qDebug() << " 数量:" << itemObj["quantity"].toInt();}if(itemObj.contains("price") && itemObj["price"].isDouble()){qDebug() << " 单价:" << itemObj["price"].toDouble() << "元";}}}}}}}}}
}
其中的解析对象或数组部分需要根据响应的JSON文件编写
JSON文件
解析后的
结语:
无论你是初学者还是有经验的开发者,我希望我的博客能对你的学习之路有所帮助。如果你觉得这篇文章有用,不妨点击收藏,或者留下你的评论分享你的见解和经验,也欢迎你对我博客的内容提出建议和问题。每一次的点赞、评论、分享和关注都是对我的最大支持,也是对我持续分享和创作的动力