diff --git a/src/blackcore/airspacemonitor.cpp b/src/blackcore/airspacemonitor.cpp index 4e49c8948..0c80a90fe 100644 --- a/src/blackcore/airspacemonitor.cpp +++ b/src/blackcore/airspacemonitor.cpp @@ -1148,16 +1148,25 @@ namespace BlackCore if (!simAircraft.isPartsSynchronized() && !isFull) { return; } CAircraftParts parts; - if (isFull) + try { - parts.convertFromJson(jsonObject); + if (isFull) + { + parts.convertFromJson(jsonObject); + } + else + { + // incremental update + parts = this->remoteAircraftParts(callsign).frontOrDefault(); + QJsonObject config = applyIncrementalObject(parts.toJson(), jsonObject); + parts.convertFromJson(config); + } } - else + catch (const CJsonException &ex) { - // incremental update - parts = this->remoteAircraftParts(callsign).frontOrDefault(); - QJsonObject config = applyIncrementalObject(parts.toJson(), jsonObject); - parts.convertFromJson(config); + CStatusMessage message = ex.toStatusMessage(this, "Invalid parts packet"); + message.setSeverity(CStatusMessage::SeverityDebug); + CLogMessage::preformatted(message); } // make sure in any case right time diff --git a/src/blackcore/application.cpp b/src/blackcore/application.cpp index 297c3f3f5..982a0b946 100644 --- a/src/blackcore/application.cpp +++ b/src/blackcore/application.cpp @@ -175,7 +175,7 @@ namespace BlackCore CApplicationInfoList CApplication::getRunningApplications() { CApplicationInfoList apps; - apps.convertFromJson(CFileUtils::readLockedFileToString(swiftDataRoot() + "apps.json")); + apps.convertFromJsonNoThrow(CFileUtils::readLockedFileToString(swiftDataRoot() + "apps.json"), {}, {}); apps.removeIf([](const CApplicationInfo &info) { return !info.processInfo().exists(); }); return apps; } diff --git a/src/blackgui/components/flightplancomponent.cpp b/src/blackgui/components/flightplancomponent.cpp index b90e6ee69..23ecb7140 100644 --- a/src/blackgui/components/flightplancomponent.cpp +++ b/src/blackgui/components/flightplancomponent.cpp @@ -413,7 +413,15 @@ namespace BlackGui } CFlightPlan fp; - fp.convertFromJson(json); + try + { + fp.convertFromJson(json); + } + catch (const CJsonException &ex) + { + m = ex.toStatusMessage(this, "Parse error in " + fileName); + break; + } this->fillWithFlightPlanData(fp); } while (false); diff --git a/src/blackgui/components/internalscomponent.cpp b/src/blackgui/components/internalscomponent.cpp index 938c32842..d983ecc39 100644 --- a/src/blackgui/components/internalscomponent.cpp +++ b/src/blackgui/components/internalscomponent.cpp @@ -124,7 +124,16 @@ namespace BlackGui CLogMessage(this).validationError("Parse error: %1") << jsonError.errorString(); return; } - parts.convertFromJson(jsonDoc.object()); + + try + { + parts.convertFromJson(jsonDoc.object()); + } + catch (const CJsonException &ex) + { + ex.toLogMessage(this, "Parse error"); + return; + } partsToGui(parts); } else diff --git a/src/blackgui/guiutility.cpp b/src/blackgui/guiutility.cpp index 6e23ad30b..92ed03282 100644 --- a/src/blackgui/guiutility.cpp +++ b/src/blackgui/guiutility.cpp @@ -149,7 +149,8 @@ namespace BlackGui if (typeName.isEmpty() || typeId == QMetaType::UnknownType) { return CVariant(); } CVariant valueVariant; - valueVariant.convertFromJson(jsonObj); + const CStatusMessage status = valueVariant.convertFromJsonNoThrow(jsonObj, {}, {}); + if (status.isFailure()) { return CVariant(); } return valueVariant; } diff --git a/src/blackmisc/datacache.cpp b/src/blackmisc/datacache.cpp index 24df4569b..34bd3220f 100644 --- a/src/blackmisc/datacache.cpp +++ b/src/blackmisc/datacache.cpp @@ -763,7 +763,7 @@ namespace BlackMisc auto json = QJsonDocument::fromJson(file.readAll()).object(); QUuid uuid(json.value("uuid").toString()); CSequence apps; - apps.convertFromJson(json.value("apps").toObject()); + auto status = apps.convertFromJsonNoThrow(json.value("apps").toObject(), this, QStringLiteral("Error in %1 apps object").arg(m_filename)); apps.removeIf([](const CProcessInfo &pi) { return ! pi.exists(); }); if (apps.isEmpty()) { uuid = CIdentifier().toUuid(); } diff --git a/src/blackmisc/valuecache.cpp b/src/blackmisc/valuecache.cpp index 25ee24595..fc3c99da2 100644 --- a/src/blackmisc/valuecache.cpp +++ b/src/blackmisc/valuecache.cpp @@ -329,7 +329,15 @@ namespace BlackMisc { CVariantMap map; map.convertFromMemoizedJson(json); - insertValues({ map, QDateTime::currentMSecsSinceEpoch() }); + if (! map.isEmpty()) { insertValues({ map, QDateTime::currentMSecsSinceEpoch() }); } + } + + CStatusMessageList CValueCache::loadFromJsonNoThrow(const QJsonObject &json, const CLogCategoryList &categories, const QString &prefix) + { + CVariantMap map; + auto messages = map.convertFromMemoizedJsonNoThrow(json, categories, prefix); + if (! map.isEmpty()) { insertValues({ map, QDateTime::currentMSecsSinceEpoch() }); } + return messages; } CStatusMessage CValueCache::saveToFiles(const QString &dir, const QString &keyPrefix) @@ -418,6 +426,7 @@ namespace BlackMisc keysInFiles.insert(filename.completeBaseName(), {}); } } + bool ok = true; for (auto it = keysInFiles.cbegin(); it != keysInFiles.cend(); ++it) { QFile file(dir + "/" + it.key() + ".json"); @@ -435,13 +444,19 @@ namespace BlackMisc return CStatusMessage(this).error("Invalid JSON format in %1") << file.fileName(); } CVariantMap temp; - temp.convertFromMemoizedJson(json.object(), it.value()); - if (it.value().isEmpty()) { temp.convertFromMemoizedJson(json.object()); } + const QString messagePrefix = QStringLiteral("Parsing %1.json").arg(it.key()); + auto messages = temp.convertFromMemoizedJsonNoThrow(json.object(), it.value(), this, messagePrefix); + if (it.value().isEmpty()) { messages.push_back(temp.convertFromMemoizedJsonNoThrow(json.object(), this, messagePrefix)); } + if (! messages.isEmpty()) + { + ok = false; + CLogMessage::preformatted(messages); + } temp.removeDuplicates(currentValues); o_values.insert(temp, QFileInfo(file).lastModified().toMSecsSinceEpoch()); } - return CStatusMessage(this).info("Loaded cache values %1 from %2") << - (keysMessage.isEmpty() ? o_values.keys().to().join(",") : keysMessage) << dir; + return CStatusMessage(this).info("Loaded cache values %1 from %2 %3") << + (keysMessage.isEmpty() ? o_values.keys().to().join(",") : keysMessage) << dir << (ok ? "successfully" : "with errors"); } void CValueCache::markAllAsSaved(const QString &keyPrefix) diff --git a/src/blackmisc/valuecache.h b/src/blackmisc/valuecache.h index 7db7140be..12c3f3755 100644 --- a/src/blackmisc/valuecache.h +++ b/src/blackmisc/valuecache.h @@ -21,7 +21,7 @@ #include "blackmisc/propertyindex.h" #include "blackmisc/range.h" #include "blackmisc/slot.h" -#include "blackmisc/statusmessage.h" +#include "blackmisc/statusmessagelist.h" #include "blackmisc/valuecacheprivate.h" #include "blackmisc/variant.h" #include "blackmisc/variantmap.h" @@ -193,9 +193,14 @@ namespace BlackMisc //! Load all values in Json format. //! Values already in the cache will remain in the cache unless they are overwritten. + //! \throws BlackMisc::CJsonException if JSON schema validation fails. //! \threadsafe void loadFromJson(const QJsonObject &json); + //! Call loadFromJson, catch any CJsonException that are thrown and return them as CStatusMessage. + //! \threadsafe + CStatusMessageList loadFromJsonNoThrow(const QJsonObject &json, const CLogCategoryList &categories, const QString &prefix); + //! Save values to Json files in a given directory. //! If prefix is provided then only those values whose keys start with that prefix. //! \threadsafe