Qt发送和解析JSON数据(通过使用qml调用js来post请求)————附带详细代码和事例

459 阅读4分钟

文章目录

0 背景

因为登陆账号和发送一些数据需要要用到http请求去发送和接受JSON数据,所以查阅了相关资料,加上自己的实践,整理出如下博文。

1 发送数据

1.1 发送和接受数据调用的js方法:

// GET
function get(url, success, failure)
{
    var xhr = new XMLHttpRequest;
    xhr.open("GET", url);
    xhr.onreadystatechange = function() {
        handleResponse(xhr, success, failure);
    }
    xhr.send();
}
// POST
function post(success, failure, url, arg,)
{

    var xhr = new XMLHttpRequest;
    xhr.open("POST", url);
    xhr.setRequestHeader("Content-Length", arg.length);
    xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded;"); //post必备
    xhr.onreadystatechange = function() {
        handleResponse(xhr, success, failure);
    }
    xhr.send(arg);
}

// 处理返回值
function handleResponse(xhr, success, failure){
//    console.log(xhr);
//    console.log(success);
//    console.log(failure);
    if (xhr.readyState == XMLHttpRequest.DONE) {
        if (xhr.status ==  200){
            if (success != null && success != undefined)
            {
                var result = xhr.responseText;
                try{
                    success(result, JSON.parse(result));
                }catch(e){
                    success(result, {});
                }
            }
        }
        else{
            if (failure != null && failure != undefined)
                failure(xhr.responseText, xhr.status);
        }
    }
}

1.2 qml(用于调用js方法)

import QtQuick 2.0
import QtQuick.Window 2.2
import "ajax.js" as Ajax

Item {

    //loginAccount
    //登录接口
    function  loginAccount(ajax, deviceid, lastloginversion, password, username, loginfield,  type) {
    //打印参数
       console.log("登录发送的参数:", ajax, deviceid, lastloginversion, password, username, loginfield,  type) //打印参数数据
        Ajax.post(
             function (data){              //成功后的回调函数
                 console.log("登录成功的返回值:", data);
					//成功后调用的槽函数:public属性
                    $Login.activeResult(data);
                    console.log('进入登录接口');

            },function (data){              //失败后的回调函数
                //失败后调用的槽函数
                  $Login.failHttpResult(data);
                console.log(data)
            },
            "请求的网址:如http://88.888.88.88/api/app/login?",
                  "ajax=" + ajax
                  + "&deviceid=" + deviceid
                  + "&lastloginversion="+ lastloginversion
                  + "&password=" + password
                  + "&username=" + username
                  + "&loginfield=" + loginfield
                  + "&type=" + type,

                  Login.invokeFunc
                  );
            }
            
    //发送json结构体
    function  sendDiscernData2(dataStruct, user_id) {
        //imageData,
       console.log("发送到游戏端的数据:", dataStruct, user_id) //打印参数数据

        Ajax.post(
            function (data){              //成功后的回调函数
               //成功调用的槽函数
                $ShowImage.activeResult2(data);
                console.log("识别接口2 success:", data);

            },function (){              //失败后的回调函数
            //失败调用的槽函数
				$ShowImage.failResult2(data);                
                console.log(" 识别接口2 error:", data);
            },
              "请求的网址",
                  "data="+dataStruct
                  +"&user_id="+user_id,

                  ShowImage.invokeFunc
                  );
            }
}

以上两个文件作为资源文件引入到项目中【一般qml和调用的js放在相同的目录下】:
在这里插入图片描述

1.3 调用方法

头文件.h中

#include <QQmlApplicationEngine>
#include <QQmlContext>
#include<QQmlComponent>
#include<QJsonObject>
#include<QJsonArray>
#include<QJsonValue>
#include<QJsonParseError>


//调登录接口
private:
    QQmlApplicationEngine engine;
    QObject *engineObject;      //指向运行的qml对象
public slots:
       /**
        * @brief 激活结果回来(可能成功,可能失败)
        * @param 返回json数组
        * @return 返回是否登录成功
        */
       void activeResult(const QVariant &);
       /**
        * @brief failHttpResult 激活结果失败
        */
       void failHttpResult(const QVariant &);

.cpp文件中:

构造函数中加入如下代码:

    //设置qml全局访问属性
    //engine.load("D:/Qfile/testAjax/file/main.qml");
    engine.rootContext()->setContextProperty("Login", this);

    //将QML中的Widget变量指向为当前类.从而使QML和widget类连接起来
    //创建qml并获取运行中的qml对象
    //QStringLiteral 宏可以在编译期把代码里的常量字符串 str 直接构造为 QString 对象
    //QQmlComponent component(&engine, QUrl("qrc:/main.qml"));
   //QQmlComponent component(&engine, "D:/Qfile/testAjax/file/main.qml");

    QQmlComponent component(&engine);
     component.loadUrl(QUrl(QStringLiteral("qrc:/http/main.qml")));

    engineObject = component.create();

   //将引用传递过去给qml进行绑定
   engine.rootContext()->setContextProperty("$Login", this);

1.4 处理JSON返回值

调用后返回的值:

 {
 "success":true,
 "jwt":"gfsdgfgs",
 "message":{"gender":0,"nation":"","birth_date":"","bio":"","msn":"","real_name":"","type":1,"devicetype":0,"password":"123455","jihuo_status":0,"server_ip":"47.108.50.146","email":"","onlineflag":0,"qq":"","salt":"","mobile":"","avatar":"","priority":0, "deviceid":"1","weixin":"","wifi_name":0,"user_id":1,"nick_name":"","wifi_password":0,"node_id":0,"username":"A1234500"},
 "devices":[{"top":28,"mj_count":108,"user_id":1,"bottom":28,"serial_number":"12345601","version_number":1,"direction":0},{"top":26,"mj_count":108,"user_id":1,"bottom":26,"serial_number":"12345602","version_number":1,"direction":1},{"top":28,"mj_count":108,"user_id":1,"bottom":28,"serial_number":"12345603","version_number":1,"direction":2},{"top":26,"mj_count":108,"user_id":1,"bottom":26,"serial_number":"12345604","version_number":1,"direction":3}]
 }

调试信息打印出的图片:
在这里插入图片描述
解析方法:

//获得返回 的值
void Login::activeResult(const QVariant &var)
{

    QVector<QPair<QString, int>>serialNumberInformation;
    QMap<QString, int> serialNumber2Direction;
    QMap<int, QString> direction2SerialNumber;
    QByteArray userAccount;
    QString wifiAccount;
    QString wifiPassword;

    int userId = 0;

     QString serial_number;
     QJsonParseError jsonError;
     QJsonDocument doucment = QJsonDocument::fromJson(var.toByteArray(), &jsonError);
     qDebug()<<"解析文档";

     if (!doucment.isNull() && (jsonError.error == QJsonParseError::NoError)) {
         qDebug()<<"解析未发生错误";
         if (doucment.isObject()) {
           qDebug()<<"JSON 文档为对象";

             QJsonObject object = doucment.object();  // 转化为对象
             /*
              * 解析单个数据
              */
             //登录成功判断
             if (object.contains("success")) {  // 包含指定的 key
                 QJsonValue value = object.value("success");  // 获取指定 key 对应的 value
                 if(value == true){
                     loginStatus = true;
                     qDebug()<<"登录成功";
                 }else{
                     loginStatus = false;
                     qDebug()<<"登录失败";
                 }
             }
             
             /*
              * 解析多个结构体
              */
             if (object.contains("devices")) {
                 QJsonValue value = object.take("devices");
                 if(value.isArray()){
                     QJsonArray arr = value.toArray();
                     qDebug() << arr.size();
                     for(int i = 0; i < arr.size(); i++)
                     {
                         QJsonValue value = arr.at(i);

                         QPair<QString, int> tempInfo;
                         tempInfo.first = value["serial_number"].toString();
                         tempInfo.second = value["direction"].toInt();
                         serialNumberInformation.push_back(tempInfo);

                         serialNumber2Direction[tempInfo.first] = tempInfo.second;
                         direction2SerialNumber[tempInfo.second] = tempInfo.first;

                         if(userId == 0){
                             userId = value["user_id"].toInt();
                         }

                         //qDebug()<<i<<":"<<value["serial_number"].toString();

                     }
                 }
             }//获得序列号
             /*
              * 解析单个
              */
             //获得账号
             if(object.contains("message")){
                 QJsonValue value = object.take("message");
                 QString tempUsername = value["username"].toString();
                 qDebug()<<"tempUsername:"<<tempUsername;

                 for (int i = 0;i < tempUsername.size();i += 2) {
                     userAccount += HexStringToByteArray(tempUsername.mid(i,2));
                 }
                // qDebug()<<"userAccount:"<<userAccount.toHex().toUpper();
                 wifiAccount = value["wifi_name"].toString();
                 qDebug()<<"wifiAccount:"<<wifiAccount;
                 wifiPassword = value["wifi_password"].toString();
                 qDebug()<<"wifiPassword:"<<wifiPassword;
             }

         }// JSON 文档为对象
     }// 解析未发生错误

}

2 发送数据

2.1 发送单个数据

    m_username = ui->accountComboBox->currentText() ;
    m_password = ui->passwordLineEdit->text();
//正式发送数据

        QVariant ajax = true;
        QVariant deviceid = 1.0;
        QVariant lastloginversion = 1.0;
        QVariant devicetype = 0;
        QVariant password = static_cast<QVariant>(m_password);
        QVariant username = static_cast<QVariant>(m_username);
        QVariant loginfield = "username";
        QVariant type =  "username";


//调用方法 发送数据 
//发送顺序要和qml传入的顺序一致
        QMetaObject::invokeMethod(engineObject, "loginAccount",
                                  Q_ARG(QVariant, ajax),
                                  Q_ARG(QVariant, deviceid),
                                  Q_ARG(QVariant, lastloginversion),
                                  Q_ARG(QVariant, password),
                                  Q_ARG(QVariant, username),
                                  Q_ARG(QVariant, loginfield),
                                  Q_ARG(QVariant, type));

2.2 发送结构体

思路:

  • 1 使用QVariantList存储QVariantMap数据
  • 2 把QVariantList转为 QJsonArray,再转为QJsonDocument,再转为QString
  • 3 去除转后的空格和换行
  • 4 调用方法发送数据
void ShowImage::sendAllDiscernCardResult()
{

    //获得时间;2020-08-27 18:56:06
    QDateTime curDateTime = QDateTime::currentDateTime();

    QVariantList varList;
    QVariantMap tempMap;

    for (int i = 1;i <= eastCardSum;i++) {
        tempMap["add_time"] = curDateTime.toString("yyyy-MM-dd hh:mm:ss");
        tempMap["chupai_direction"] = "1";
        tempMap["direction"] = 0;
        tempMap["id"] = 0;
        tempMap["mahjong_number"] = i;//((i % 2 == 0) ? (i - 1): (i + 1))
        tempMap["napai_direction"] = 1;
        tempMap["result"] =  recognitionCardData[0][0][i];
        tempMap["serial_number"] = usrId;
        tempMap["yuce_direction"] = 1;
        tempMap["zhuo_id"] = 1;

        varList<<tempMap;
    }

    for (int i = 1;i <= southCardSum;i++) {
        tempMap["add_time"] = curDateTime.toString("yyyy-MM-dd hh:mm:ss");
        tempMap["chupai_direction"] = "1";
        tempMap["direction"] = 1;
        tempMap["id"] = 0;
        tempMap["mahjong_number"] = i;
        tempMap["napai_direction"] = 1;
        tempMap["result"] =  recognitionCardData[0][1][i];
        tempMap["serial_number"] = usrId;
        tempMap["yuce_direction"] = 1;
        tempMap["zhuo_id"] = 1;

        varList<<tempMap;
    }

    for (int i = 1;i <= westCardSum;i++) {
        tempMap["add_time"] = curDateTime.toString("yyyy-MM-dd hh:mm:ss");
        tempMap["chupai_direction"] = "1";
        tempMap["direction"] = 2;
        tempMap["id"] = 0;
        tempMap["mahjong_number"] = i;
        tempMap["napai_direction"] = 1;
        tempMap["result"] =  recognitionCardData[0][2][i];
        tempMap["serial_number"] = usrId;
        tempMap["yuce_direction"] = 1;
        tempMap["zhuo_id"] = 1;

        varList<<tempMap;
    }

    for (int i = 1;i <= northCardSum;i++) {
        tempMap["add_time"] = curDateTime.toString("yyyy-MM-dd hh:mm:ss");
        tempMap["chupai_direction"] = "1";
        tempMap["direction"] = 3;
        tempMap["id"] = 0;
        tempMap["mahjong_number"] = i;
        tempMap["napai_direction"] = 1;
        tempMap["result"] =  recognitionCardData[0][3][i];
        tempMap["serial_number"] = usrId;
        tempMap["yuce_direction"] = 1;
        tempMap["zhuo_id"] = 1;

        varList<<tempMap;
    }


   QJsonArray dataArray = QJsonArray::fromVariantList(varList);
 // qDebug()<<"dataArray:"<<dataArray;
   QJsonDocument dataDoc;
   dataDoc.setArray(dataArray);
   //
   QString dataString = static_cast<QString>(dataDoc.toJson());
   //去换行
   dataString.remove(QChar('\n'), Qt::CaseInsensitive);
   //去空格
   dataString.remove(QChar('\040'), Qt::CaseInsensitive);
   QVariant dataStruct = static_cast<QVariant>(dataString);

   usrId = 1;

   QVariant user_id = static_cast<QVariant>(usrId);
   
   //发送数据
   QMetaObject::invokeMethod(engineObject, "sendDiscernData2",
                             Q_ARG(QVariant, dataStruct),
                             Q_ARG(QVariant, user_id));

}

3 打包时,容易出现的错误

一般qt程序打包,很多人喜欢使用windeployqt 程序路径,但是这样打包出来的程序,虽然release的版本不会报错(qt会到默认的路径下去寻找qml环境文件),但是运行程序却运行不起来,原因在于qml的环境没有打包好(一般),容易出现诸如下面的错误:
在这里插入图片描述
解决的方法是:使用如下的命令:

windeployqt.exe --qmldir ./qml 程序路径