From bbdbd26f8274600a0123e47c4f784e8f71f06019 Mon Sep 17 00:00:00 2001 From: Mathew Sutcliffe Date: Sun, 18 Dec 2016 04:44:25 +0000 Subject: [PATCH] refs #815 Throw CJsonException when required JSON objects are not found. --- src/blackmisc/containerbase.h | 12 +++-- src/blackmisc/dictionary.h | 19 ++++++-- src/blackmisc/json.h | 14 +++++- src/blackmisc/network/url.cpp | 5 +- src/blackmisc/pq/physicalquantity.cpp | 10 ++-- .../simulation/aircraftmodellist.cpp | 29 ++++++++++-- .../simulation/simulatorplugininfo.cpp | 3 ++ src/blackmisc/variant.cpp | 47 ++++++++++++------- src/blackmisc/variantmap.cpp | 10 +++- .../weather/weatherdataplugininfo.cpp | 3 ++ 10 files changed, 112 insertions(+), 40 deletions(-) diff --git a/src/blackmisc/containerbase.h b/src/blackmisc/containerbase.h index 916a3a2c6..2b1ba1099 100644 --- a/src/blackmisc/containerbase.h +++ b/src/blackmisc/containerbase.h @@ -122,13 +122,17 @@ namespace BlackMisc void convertFromJson(const QJsonObject &json) { derived().clear(); - QJsonArray array = json.value("containerbase").toArray(); + QJsonValue value = json.value("containerbase"); + if (value.isUndefined()) { throw CJsonException("Missing 'containerbase'"); } + QJsonArray array = value.toArray(); + int index = 0; for (auto i = array.begin(); i != array.end(); ++i) { + CJsonScope scope("containerbase", index++); QJsonValueRef ref = (*i); - T value; - ref >> value; - derived().insert(value); + T val; + ref >> val; + derived().insert(std::move(val)); } } diff --git a/src/blackmisc/dictionary.h b/src/blackmisc/dictionary.h index deb9130f6..00379e8b1 100644 --- a/src/blackmisc/dictionary.h +++ b/src/blackmisc/dictionary.h @@ -257,7 +257,10 @@ namespace BlackMisc //! \copydoc BlackMisc::Mixin::JsonByMetaClass::convertFromJson void convertFromJson(const QJsonObject &json) { - QJsonArray array = json.value("associativecontainerbase").toArray(); + QJsonValue value = json.value("associativecontainerbase"); + if (value.isUndefined()) { throw CJsonException("Missing 'associativecontainerbase'"); } + QJsonArray array = value.toArray(); + int index = 0; for (auto it = array.begin(); it != array.end(); ++it) { QJsonValueRef jsonKey = (*it); @@ -265,10 +268,16 @@ namespace BlackMisc if (it == array.end()) { qWarning("Odd number of elements in CDictionary::convertFromJson"); return; } QJsonValueRef jsonValue = (*it); Key key; - Value value; - jsonKey >> key; - jsonValue >> value; - m_impl.insert(key, value); + Value val; + { + CJsonScope scope("associativecontainerbase", 2 * index); + jsonKey >> key; + } + { + CJsonScope scope("associativecontainerbase", 2 * index++ + 1); + jsonValue >> val; + } + m_impl.insert(std::move(key), std::move(val)); } } diff --git a/src/blackmisc/json.h b/src/blackmisc/json.h index 63ff7d2c7..cfd66d11a 100644 --- a/src/blackmisc/json.h +++ b/src/blackmisc/json.h @@ -16,6 +16,7 @@ #include "blackmisc/fileutils.h" #include "blackmisc/inheritancetraits.h" #include "blackmisc/metaclass.h" +#include "blackmisc/jsonexception.h" #include #include @@ -375,8 +376,17 @@ namespace BlackMisc auto meta = introspect().without(MetaFlags()); meta.forEachMemberName(*derived(), [ & ](auto & member, CExplicitLatin1String name) { - auto it = json.find(name); - if (it != json.end()) { it.value() >> member; } + const auto value = json.value(name); + if (value.isUndefined()) + { + constexpr bool required = false; // \todo add RequiredForJson flag in metaclass system + if (required) { throw CJsonException(QStringLiteral("Missing required member '%1'").arg(name.m_latin1)); } + } + else + { + CJsonScope scope(name.m_latin1); + value >> member; + } }); } diff --git a/src/blackmisc/network/url.cpp b/src/blackmisc/network/url.cpp index 3448e7f40..1ceeeef3e 100644 --- a/src/blackmisc/network/url.cpp +++ b/src/blackmisc/network/url.cpp @@ -194,8 +194,9 @@ namespace BlackMisc void CUrl::convertFromJson(const QJsonObject &json) { - QString url(json.value("url").toString()); - this->setFullUrl(url); + const QJsonValue value = json.value("url"); + if (value.isUndefined()) { throw CJsonException("Missing 'url'"); } + this->setFullUrl(value.toString()); } int CUrl::protocolToDefaultPort(const QString &protocol) diff --git a/src/blackmisc/pq/physicalquantity.cpp b/src/blackmisc/pq/physicalquantity.cpp index e99a58d63..b6c5d1dd4 100644 --- a/src/blackmisc/pq/physicalquantity.cpp +++ b/src/blackmisc/pq/physicalquantity.cpp @@ -348,9 +348,13 @@ namespace BlackMisc template void CPhysicalQuantity::convertFromJson(const QJsonObject &json) { - const QString unitSymbol = json.value("unit").toString(); - this->setUnitBySymbol(unitSymbol); - this->m_value = json.value("value").toDouble(); + const QJsonValue unit = json.value("unit"); + const QJsonValue value = json.value("value"); + if (unit.isUndefined()) { throw CJsonException("Missing 'unit'"); } + if (value.isUndefined()) { throw CJsonException("Missing 'value'"); } + + this->setUnitBySymbol(unit.toString()); + this->m_value = value.toDouble(); } template diff --git a/src/blackmisc/simulation/aircraftmodellist.cpp b/src/blackmisc/simulation/aircraftmodellist.cpp index addfb5e81..ac6e89c54 100644 --- a/src/blackmisc/simulation/aircraftmodellist.cpp +++ b/src/blackmisc/simulation/aircraftmodellist.cpp @@ -698,13 +698,34 @@ namespace BlackMisc void CAircraftModelList::convertFromMemoizedJson(const QJsonObject &json) { clear(); - QJsonArray array = json.value("containerbase").toArray(); + QJsonValue value = json.value("containerbase"); + if (value.isUndefined()) { throw CJsonException("Missing 'containerbase'"); } + QJsonArray array = value.toArray(); + CAircraftModel::MemoHelper::CUnmemoizer helper; - helper.getTable().convertFromJson(json.value("aircraftIcaos").toObject()); - helper.getTable().convertFromJson(json.value("liveries").toObject()); - helper.getTable().convertFromJson(json.value("distributors").toObject()); + QJsonValue aircraftIcaos = json.value("aircraftIcaos"); + QJsonValue liveries = json.value("liveries"); + QJsonValue distributors = json.value("distributors"); + if (aircraftIcaos.isUndefined()) { throw CJsonException("Missing 'aircraftIcaos'"); } + if (liveries.isUndefined()) { throw CJsonException("Missing 'liveries'"); } + if (distributors.isUndefined()) { throw CJsonException("Missing 'distributors'"); } + { + CJsonScope scope("aircraftIcaos"); + helper.getTable().convertFromJson(aircraftIcaos.toObject()); + } + { + CJsonScope scope("liveries"); + helper.getTable().convertFromJson(liveries.toObject()); + } + { + CJsonScope scope("distributors"); + helper.getTable().convertFromJson(distributors.toObject()); + } + + int index = 0; for (auto i = array.begin(); i != array.end(); ++i) { + CJsonScope scope("containerbase", index++); CAircraftModel value; value.convertFromMemoizedJson(i->toObject(), helper); insert(value); diff --git a/src/blackmisc/simulation/simulatorplugininfo.cpp b/src/blackmisc/simulation/simulatorplugininfo.cpp index 03e1bbfa0..4600b51c8 100644 --- a/src/blackmisc/simulation/simulatorplugininfo.cpp +++ b/src/blackmisc/simulation/simulatorplugininfo.cpp @@ -27,7 +27,10 @@ namespace BlackMisc { if (json.contains("IID")) // comes from the plugin { + if (! json.contains("MetaData")) { throw CJsonException("Missing 'MetaData'"); } + // json data is already validated by CPluginManagerSimulator + CJsonScope scope("MetaData"); CValueObject::convertFromJson(json["MetaData"].toObject()); m_valid = true; } diff --git a/src/blackmisc/variant.cpp b/src/blackmisc/variant.cpp index 7d0ab72ca..5033502d4 100644 --- a/src/blackmisc/variant.cpp +++ b/src/blackmisc/variant.cpp @@ -156,38 +156,43 @@ namespace BlackMisc { // Remark: Names "type" and "value" are also used for drag and drop // Changing the names here requires the change for drag and drop too - QString typeName = json.value("type").toString(); + QJsonValue typeValue = json.value("type"); + if (typeValue.isUndefined()) { throw CJsonException("Missing 'type'"); } + QString typeName = typeValue.toString(); if (typeName.isEmpty()) { m_v.clear(); return; } int typeId = QMetaType::type(qPrintable(typeName)); + QJsonValue value = json.value("value"); + if (value.isUndefined() && typeId != QVariant::Invalid) { throw CJsonException("Missing 'value'"); } switch (typeId) { case QVariant::Invalid: CLogMessage(this).warning("Invalid type for fromJson: %1") << typeName; m_v.clear(); break; - case QVariant::Int: m_v.setValue(json.value("value").toInt()); break; - case QVariant::UInt: m_v.setValue(json.value("value").toInt()); break; - case QVariant::Bool: m_v.setValue(json.value("value").toBool()); break; - case QVariant::Double: m_v.setValue(json.value("value").toDouble()); break; - case QVariant::LongLong: m_v.setValue(json.value("value").toInt()); break; // QJsonValue has no toLongLong() method??? - case QVariant::ULongLong: m_v.setValue(json.value("value").toInt()); break; - case QVariant::String: m_v.setValue(json.value("value").toString()); break; - case QVariant::Char: m_v.setValue(json.value("value").toString().size() > 0 ? json.value("value").toString().at(0) : '\0'); break; - case QVariant::ByteArray: m_v.setValue(json.value("value").toString().toLatin1()); break; - case QVariant::DateTime: m_v.setValue(QDateTime::fromString(json.value("value").toString(), Qt::ISODate)); break; - case QVariant::Date: m_v.setValue(QDate::fromString(json.value("value").toString(), Qt::ISODate)); break; - case QVariant::Time: m_v.setValue(QTime::fromString(json.value("value").toString(), Qt::ISODate)); break; - case QVariant::StringList: m_v.setValue(QVariant(json.value("value").toArray().toVariantList()).toStringList()); break; + case QVariant::Int: m_v.setValue(value.toInt()); break; + case QVariant::UInt: m_v.setValue(value.toInt()); break; + case QVariant::Bool: m_v.setValue(value.toBool()); break; + case QVariant::Double: m_v.setValue(value.toDouble()); break; + case QVariant::LongLong: m_v.setValue(value.toInt()); break; // QJsonValue has no toLongLong() method??? + case QVariant::ULongLong: m_v.setValue(value.toInt()); break; + case QVariant::String: m_v.setValue(value.toString()); break; + case QVariant::Char: m_v.setValue(value.toString().size() > 0 ? value.toString().at(0) : '\0'); break; + case QVariant::ByteArray: m_v.setValue(value.toString().toLatin1()); break; + case QVariant::DateTime: m_v.setValue(QDateTime::fromString(value.toString(), Qt::ISODate)); break; + case QVariant::Date: m_v.setValue(QDate::fromString(value.toString(), Qt::ISODate)); break; + case QVariant::Time: m_v.setValue(QTime::fromString(value.toString(), Qt::ISODate)); break; + case QVariant::StringList: m_v.setValue(QVariant(value.toArray().toVariantList()).toStringList()); break; default: try { auto *meta = Private::getValueObjectMetaInfo(typeId); if (meta) { + CJsonScope scope("value"); m_v = QVariant(typeId, nullptr); - meta->convertFromJson(json.value("value").toObject(), data()); + meta->convertFromJson(value.toObject(), data()); } else if (QMetaType::hasRegisteredConverterFunction(qMetaTypeId(), typeId)) { - m_v.setValue(json.value("value").toString()); + m_v.setValue(value.toString()); if (! m_v.convert(typeId)) { CLogMessage(this).warning("Failed to convert from JSON string"); @@ -231,15 +236,21 @@ namespace BlackMisc void CVariant::convertFromMemoizedJson(const QJsonObject &json) { - QString typeName = json.value("type").toString(); + QJsonValue typeValue = json.value("type"); + if (typeValue.isUndefined()) { throw CJsonException("Missing 'type'"); } + QString typeName = typeValue.toString(); if (typeName.isEmpty()) { m_v.clear(); return; } int typeId = QMetaType::type(qPrintable(typeName)); auto *meta = Private::getValueObjectMetaInfo(typeId); if (meta) { + QJsonValue value = json.value("value"); + if (value.isUndefined()) { throw CJsonException("Missing 'value'"); } + + CJsonScope scope("value"); m_v = QVariant(typeId, nullptr); - meta->convertFromMemoizedJson(json.value("value").toObject(), data()); + meta->convertFromMemoizedJson(value.toObject(), data()); } else { diff --git a/src/blackmisc/variantmap.cpp b/src/blackmisc/variantmap.cpp index 652d57ef8..1c3f07e38 100644 --- a/src/blackmisc/variantmap.cpp +++ b/src/blackmisc/variantmap.cpp @@ -35,9 +35,11 @@ namespace BlackMisc clear(); for (auto it = json.begin(); it != json.end(); ++it) { + const QString key = it.key(); + CJsonScope scope(key); CVariant value; value.convertFromJson(it.value().toObject()); - implementationOf(*this).insert(cend(), it.key(), value); + implementationOf(*this).insert(cend(), key, value); } } @@ -48,6 +50,7 @@ namespace BlackMisc { auto value = json.value(key); if (value.isUndefined()) { continue; } + CJsonScope scope(key); CVariant var; var.convertFromJson(value.toObject()); insert(key, var); @@ -75,9 +78,11 @@ namespace BlackMisc clear(); for (auto it = json.begin(); it != json.end(); ++it) { + const QString key = it.key(); + CJsonScope scope(key); CVariant value; value.convertFromMemoizedJson(it.value().toObject()); - implementationOf(*this).insert(cend(), it.key(), value); + implementationOf(*this).insert(cend(), key, value); } } @@ -88,6 +93,7 @@ namespace BlackMisc { auto value = json.value(key); if (value.isUndefined()) { continue; } + CJsonScope scope(key); CVariant var; var.convertFromMemoizedJson(value.toObject()); insert(key, var); diff --git a/src/blackmisc/weather/weatherdataplugininfo.cpp b/src/blackmisc/weather/weatherdataplugininfo.cpp index 50bd42ab6..fc29f0137 100644 --- a/src/blackmisc/weather/weatherdataplugininfo.cpp +++ b/src/blackmisc/weather/weatherdataplugininfo.cpp @@ -26,7 +26,10 @@ namespace BlackMisc { if (json.contains("IID")) // comes from the plugin { + if (! json.contains("MetaData")) { throw CJsonException("Missing 'MetaData'"); } + // json data is already validated by CPluginManagerWeatherData + CJsonScope scope("MetaData"); CValueObject::convertFromJson(json["MetaData"].toObject()); m_valid = true; }