Skip to content
Snippets Groups Projects
JsonHelper.cc 10 KiB
Newer Older
  • Learn to ignore specific revisions
  • /****************************************************************************
     *
     *   (c) 2009-2016 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org>
     *
     * QGroundControl is licensed according to the terms in the file
     * COPYING.md in the root of the source code directory.
     *
     ****************************************************************************/
    
    Don Gagne's avatar
    Don Gagne committed
    
    
    #include "JsonHelper.h"
    
    #include <QJsonArray>
    
    #include <QJsonParseError>
    
    #include <QObject>
    #include <QRegularExpression>
    #include <QRegularExpressionMatch>
    
    Don Gagne's avatar
    Don Gagne committed
    
    
    const char* JsonHelper::_enumStringsJsonKey =       "enumStrings";
    const char* JsonHelper::_enumValuesJsonKey =        "enumValues";
    const char* JsonHelper::jsonVersionKey =            "version";
    const char* JsonHelper::jsonGroundStationKey =      "groundStation";
    const char* JsonHelper::jsonGroundStationValue =    "QGroundControl";
    const char* JsonHelper::jsonFileTypeKey =           "fileType";
    
    Don Gagne's avatar
    Don Gagne committed
    bool JsonHelper::validateRequiredKeys(const QJsonObject& jsonObject, const QStringList& keys, QString& errorString)
    {
        QString missingKeys;
    
        foreach(const QString& key, keys) {
            if (!jsonObject.contains(key)) {
                if (!missingKeys.isEmpty()) {
                    missingKeys += QStringLiteral(", ");
                }
                missingKeys += key;
            }
        }
    
        if (missingKeys.count() != 0) {
    
            errorString = QObject::tr("The following required keys are missing: %1").arg(missingKeys);
    
    Don Gagne's avatar
    Don Gagne committed
            return false;
        }
    
        return true;
    }
    
    
    bool JsonHelper::loadGeoCoordinate(const QJsonValue&    jsonValue,
                                       bool                 altitudeRequired,
                                       QGeoCoordinate&      coordinate,
                                       QString&             errorString)
    
    Don Gagne's avatar
    Don Gagne committed
    {
        if (!jsonValue.isArray()) {
    
            errorString = QObject::tr("value for coordinate is not array");
    
    Don Gagne's avatar
    Don Gagne committed
            return false;
        }
    
        QJsonArray coordinateArray = jsonValue.toArray();
        int requiredCount = altitudeRequired ? 3 : 2;
        if (coordinateArray.count() != requiredCount) {
    
            errorString = QObject::tr("Coordinate array must contain %1 values").arg(requiredCount);
    
    Don Gagne's avatar
    Don Gagne committed
            return false;
        }
    
    
    Don Gagne's avatar
    Don Gagne committed
        foreach(const QJsonValue& jsonValue, coordinateArray) {
            if (jsonValue.type() != QJsonValue::Double) {
    
                errorString = QObject::tr("Coordinate array may only contain double values, found: %1").arg(jsonValue.type());
    
    Don Gagne's avatar
    Don Gagne committed
                return false;
            }
        }
    
    
    Don Gagne's avatar
    Don Gagne committed
        coordinate = QGeoCoordinate(coordinateArray[0].toDouble(), coordinateArray[1].toDouble());
        if (altitudeRequired) {
            coordinate.setAltitude(coordinateArray[2].toDouble());
        }
    
        if (!coordinate.isValid()) {
    
            errorString = QObject::tr("Coordinate is invalid: %1").arg(coordinate.toString());
    
    Don Gagne's avatar
    Don Gagne committed
            return false;
        }
    
        return true;
    }
    
    void JsonHelper::saveGeoCoordinate(const QGeoCoordinate&    coordinate,
                                       bool                     writeAltitude,
                                       QJsonValue&              jsonValue)
    
    {
        QJsonArray coordinateArray;
    
        coordinateArray << coordinate.latitude() << coordinate.longitude();
        if (writeAltitude) {
            coordinateArray << coordinate.altitude();
        }
    
        jsonValue = QJsonValue(coordinateArray);
    }
    
    bool JsonHelper::validateKeyTypes(const QJsonObject& jsonObject, const QStringList& keys, const QList<QJsonValue::Type>& types, QString& errorString)
    
    Don Gagne's avatar
    Don Gagne committed
    {
        for (int i=0; i<keys.count(); i++) {
            if (jsonObject.contains(keys[i])) {
                if (jsonObject.value(keys[i]).type() != types[i]) {
    
                    errorString  = QObject::tr("Incorrect type key:type:expected %1 %2 %3").arg(keys[i]).arg(jsonObject.value(keys[i]).type()).arg(types[i]);
    
    Don Gagne's avatar
    Don Gagne committed
                    return false;
                }
            }
        }
    
        return true;
    }
    
    
    bool JsonHelper::parseEnum(const QJsonObject& jsonObject, QStringList& enumStrings, QStringList& enumValues, QString& errorString)
    
    Don Gagne's avatar
    Don Gagne committed
    {
        enumStrings = jsonObject.value(_enumStringsJsonKey).toString().split(",", QString::SkipEmptyParts);
        enumValues = jsonObject.value(_enumValuesJsonKey).toString().split(",", QString::SkipEmptyParts);
    
        if (enumStrings.count() != enumValues.count()) {
    
            errorString = QObject::tr("enum strings/values count mismatch strings:values %1:%2").arg(enumStrings.count()).arg(enumValues.count());
    
    Don Gagne's avatar
    Don Gagne committed
            return false;
        }
    
        return true;
    }
    
    
    bool JsonHelper::isJsonFile(const QByteArray& bytes, QJsonDocument& jsonDoc)
    {
        QJsonParseError error;
    
        jsonDoc = QJsonDocument::fromJson(bytes, &error);
    
        if (error.error == QJsonParseError::NoError) {
            return true;
        }
    
        if (error.error == QJsonParseError::MissingObject && error.offset == 0) {
            return false;
        }
    
        return true;
    }
    
    
    bool JsonHelper::validateQGCJsonFile(const QJsonObject& jsonObject,
                                         const QString&     expectedFileType,
                                         int                supportedMajorVersion,
                                         int                supportedMinorVersion,
                                         int&               fileMajorVersion,
                                         int&               fileMinorVersion,
                                         QString&           errorString)
    {
        // Check for required keys
        QStringList requiredKeys = { jsonVersionKey, jsonFileTypeKey };
        if (!validateRequiredKeys(jsonObject, requiredKeys, errorString)) {
            return false;
        }
    
        // Validate base key types
        QList<QJsonValue::Type> typeList = { QJsonValue::String, QJsonValue::String };
        if (!validateKeyTypes(jsonObject, requiredKeys, typeList, errorString)) {
            return false;
        }
    
        // Make sure file type is correct
        QString fileTypeValue = jsonObject[jsonFileTypeKey].toString();
        if (fileTypeValue != expectedFileType) {
            errorString = QObject::tr("Incorrect file type key expected:%1 actual:%2").arg(expectedFileType).arg(fileTypeValue);
            return false;
        }
    
        // Parse and validate version
    
        QString incorrectVersionFormatErrorString = QObject::tr("Incorrectly formatted version value");
        QRegularExpression versionRegExp("(\\d+).(\\d+)");
        QRegularExpressionMatch match = versionRegExp.match(jsonObject[jsonVersionKey].toString());
        if (!match.hasMatch()) {
            errorString = incorrectVersionFormatErrorString;
            return false;
        }
        QStringList versionParts = match.capturedTexts();
        if (versionParts.count() != 3) {
            errorString = incorrectVersionFormatErrorString;
            return false;
        }
    
        fileMajorVersion = versionParts[0].toInt();
        fileMinorVersion = versionParts[0].toInt();
        if (fileMajorVersion > supportedMajorVersion || (fileMajorVersion == supportedMajorVersion && fileMinorVersion > supportedMinorVersion)) {
            errorString = QObject::tr("File version (%1.%2) is larger than current supported version (%3.%4)").arg(fileMajorVersion).arg(fileMinorVersion).arg(supportedMajorVersion).arg(supportedMinorVersion);
            return false;
        }
    
        return true;
    }
    
    bool JsonHelper::loadGeoCoordinateArray(const QJsonValue&   jsonValue,
                                            bool                altitudeRequired,
                                            QVariantList&       rgVarPoints,
                                            QString&            errorString)
    {
        if (!jsonValue.isArray()) {
            errorString = QObject::tr("value for coordinate array is not array");
            return false;
        }
        QJsonArray rgJsonPoints = jsonValue.toArray();
    
        rgVarPoints.clear();
        for (int i=0; i<rgJsonPoints.count(); i++) {
            QGeoCoordinate coordinate;
    
            if (!JsonHelper::loadGeoCoordinate(rgJsonPoints[i], altitudeRequired, coordinate, errorString)) {
                return false;
            }
            rgVarPoints.append(QVariant::fromValue(coordinate));
        }
    
        return true;
    }
    
    bool JsonHelper::loadGeoCoordinateArray(const QJsonValue&       jsonValue,
                                            bool                    altitudeRequired,
                                            QList<QGeoCoordinate>&  rgPoints,
                                            QString&                errorString)
    {
        QVariantList rgVarPoints;
    
        if (!loadGeoCoordinateArray(jsonValue, altitudeRequired, rgVarPoints, errorString)) {
            return false;
        }
    
        rgPoints.clear();
        for (int i=0; i<rgVarPoints.count(); i++) {
            rgPoints.append(rgVarPoints[i].value<QGeoCoordinate>());
        }
    
        return true;
    }
    
    void JsonHelper::saveGeoCoordinateArray(const QVariantList& rgVarPoints,
                                            bool                writeAltitude,
                                            QJsonValue&         jsonValue)
    {
        QJsonArray rgJsonPoints;
    
        // Add all points to the array
        for (int i=0; i<rgVarPoints.count(); i++) {
            QJsonValue jsonPoint;
    
            JsonHelper::saveGeoCoordinate(rgVarPoints[i].value<QGeoCoordinate>(), writeAltitude, jsonPoint);
            rgJsonPoints.append(jsonPoint);
        }
    
        jsonValue = rgJsonPoints;
    }
    
    void JsonHelper::saveGeoCoordinateArray(const QList<QGeoCoordinate>&    rgPoints,
                                            bool                            writeAltitude,
                                            QJsonValue&                     jsonValue)
    {
        QVariantList rgVarPoints;
    
        for (int i=0; i<rgPoints.count(); i++) {
            rgVarPoints.append(QVariant::fromValue(rgPoints[i]));
        }
        return saveGeoCoordinateArray(rgVarPoints, writeAltitude, jsonValue);
    }
    
    Don Gagne's avatar
    Don Gagne committed
    
    bool JsonHelper::validateKeys(const QJsonObject& jsonObject, const QList<JsonHelper::KeyValidateInfo>& keyInfo, QString& errorString)
    {
        QStringList             keyList;
        QList<QJsonValue::Type> typeList;
    
        for (int i=0; i<keyInfo.count(); i++) {
            if (keyInfo[i].required) {
                keyList.append(keyInfo[i].key);
            }
        }
        if (!validateRequiredKeys(jsonObject, keyList, errorString)) {
            return false;
        }
    
        keyList.clear();
        for (int i=0; i<keyInfo.count(); i++) {
            keyList.append(keyInfo[i].key);
            typeList.append(keyInfo[i].type);
        }
        return validateKeyTypes(jsonObject, keyList, typeList, errorString);
    }