在Qt中从QJsonArray构造QString

时间:2018-04-05 06:43:25

标签: qt c++11 qstring qjson qt5.10

尝试从QString的值构建QJsonArray时,出现以下错误:

error: passing 'const QString' as 'this' argument discards qualifiers [-fpermissive]

Dunno我在这段代码中弄错了:

QString <CLASS_NAME>::getData(QString callerValue) {
    QString BASE_URL = "<URL>";

    QString stringToReturn = "";
    QObject::connect(manager, &QNetworkAccessManager::finished, this, [=](QNetworkReply *reply) {
      QByteArray barr = reply->readAll();
      QJsonParseError jpe;
      QJsonDocument jdoc = QJsonDocument::fromJson(barr, &jpe);
      QJsonArray synonymsArray = jdoc.array();
      foreach (const QJsonValue &jv, synonymsArray) {
        QJsonObject jo = jv.toObject();
        QString s = jo.value("<VALUE>").toString();
        stringToReturn.append(s + ", "); /* ERROR: The error above is from this line... */
      }
    }
    );
    request.setUrl(QUrl(BASE_URL + callerValue));
    manager->get(request);
    return stringToReturn;
  }

2 个答案:

答案 0 :(得分:2)

这是另一个经典的“我希望世界同步”的问题。你不能这样编码。 getData方法无法按您希望的方式编写。 getData以这种方式阻止,这非常浪费并且可能导致有趣的问题 - 而不是最后一个是可怕的用户体验。

根据您的应用程序,可能会有几种可能的修复方法:

  1. redo getData使用协程和co_yield隐式延续传递样式 - 这是未来,只能在最新的编译器上完成,除非你使用诸如boost协程之类的黑客。 / p>

  2. 以明确的连续传递方式重做getData

  3. 以懒惰样式重做getData,并在数据可用时发出通知,

  4. 有一个明确的状态机来处理代码的进度。

  5. 延续传递样式需要的变化最少。另请注意其他修复 - 最值得注意的是,您不应该使用QNetworkAccessManager的信号:您只对这一个查询的结果感兴趣,而不是每个查询!仅当您确实拥有可以处理所有或至少最常见请求的中心点时,捕获QNetworkAccessManager::finished信号才有用。在这种情况下,这是一个明智的优化:将几个malloc s的开销添加到一个无连接对象的第一个连接。

    void Class::getData(const QString &urlSuffix, std::function<void(const QString &)> cont) {
      auto const urlString = QStringLiteral("URL%1").arg(urlSuffix);
      QNetworkRequest request(QUrl(urlString));
      auto *reply = m_manager.get(request);
      QObject::connect(reply, &QNetworkReply::finished, this, [=]{
        QString result;
        auto data = reply->readAll();
        QJsonParseError jpe;
        auto jdoc = QJsonDocument::fromJson(data, &jpe);
        auto const synonyms = jdoc.array();
        for (auto &value : synonyms) {
          auto object = value.toObject();
          auto s = object.value("<VALUE">).toString();
          if (!result.isEmpty())
            result.append(QLatin1String(", "))
          result.append(s);
        }
        reply->deleteLater();
        cont(result);
      });
    }
    

    懒惰样式要求使用getData的代码可以重新启动,并允许继续传递,只要延续连接到信号:

    class Class : public QObject {
      Q_OBJECT
      QString m_cachedData;
      QNetworkAccessManager m_manager{this};
      Q_SIGNAL void dataAvailable(const QString &);
      ...
    };
    
    QString Class::getData(const QString &urlSuffix) {
      if (!m_cachedData.isEmpty())
        return m_cachedData;
    
      auto const urlString = QStringLiteral("URL%1").arg(urlSuffix);
      QNetworkRequest request(QUrl(urlString));
      auto *reply = m_manager.get(request);
      QObject::connect(reply, &QNetworkReply::finished, this, [=]{
        m_cachedData.clear();
        auto data = reply->readAll();
        QJsonParseError jpe;
        auto jdoc = QJsonDocument::fromJson(data, &jpe);
        auto const synonyms = jdoc.array();
        for (auto &value : synonyms) {
          auto object = value.toObject();
          auto s = object.value("<VALUE">).toString();
          if (!m_cachedData.isEmpty())
            m_cachedData.append(QLatin1String(", "))
          m_cachedData.append(s);
        }
        reply->deleteLater();
        emit dataAvailable(m_cachedData);
      });
      return {};
    }
    

    状态机正式化了州的进展:

    class Class : public QObject {
      Q_OBJECT
      QStateMachine m_sm{this};
      QNetworkAccessManager m_manager{this};
      QPointer<QNetworkReply> m_reply;
      QState s_idle{&m_sm}, s_busy{&m_sm}, s_done{&m_sm};
      Q_SIGNAL void to_busy();
      void getData(const QString &);
      ...
    
    };
    
    Class::Class(QObject * parent) : QObject(parent) {
      m_sm.setInitialState(&s_idle);
      s_idle.addTransition(this, &Class::to_busy, &s_busy);
      s_done.addTransition(&s_idle);
      m_sm.start();
    }
    
    void Class::getData(const QString &urlSuffix) {
      static char const kInit[] = "initialized";
      auto const urlString = QStringLiteral("URL%1").arg(urlSuffix);
      QNetworkRequest request(QUrl(urlString));
      m_reply = m_manager.get(request);
      s_busy.addTransition(reply, &QNetworkReply::finished, &s_done);
      to_busy();
      if (!s_done.property(kInit).toBool()) {
        QObject::connect(&s_done, &QState::entered, this, [=]{
          QString result;
          auto data = m_reply->readAll();
          QJsonParseError jpe;
          auto jdoc = QJsonDocument::fromJson(data, &jpe);
          auto const synonyms = jdoc.array();
          for (auto &value : synonyms) {
            auto object = value.toObject();
            auto s = object.value("<VALUE">).toString();
            if (!result.isEmpty())
              result.append(QLatin1String(", "))
            result.append(s);
          }
          m_reply->deleteLater();
        });
        s_done.setProperty(kInit, true);
      }
    }
    

答案 1 :(得分:1)

当然这是错误的:stringToReturn被声明为getData中的局部变量,如果你使用[=]它将是const并且它会在函数对象时死掉# 39;通过finished信号调用,如果通过引用捕获。 Lambda表达式中会有一个悬空引用。放置变量作为此函数对象的输出是个不好的地方,该变量在从getData返回时停止存在。

此处由lambda表达式创建的函数对象在 getData返回后称为。当信号因发送请求而被触发时,它将被称为 sometime ,它是一个异步处理程序。在这种情况下,getData不是lambda的调用者。 lambda的调用者是信号槽系统。如果getData没有显式调用lambda,我们无法保证在本地存储的生命周期结束之前返回函数对象。

如果你可以保证thisthis个实例)仍然是“活着的”,那么这里可能的回忆就是使用<CLASS_NAME>的字段。何时finished()被解雇。本质上这是&#34; getData&#34;除非你在请求完成之前暂停它(这会使异步方法失败),否则无法返回该值。

实际上,当finished被触发时,QNetworkReply::finished会被触发,QNetworkAccessManager ::只是发布请求并立即返回所述回复。收到数据时会触发信号(发出readyRead)