5#include <QtCore/QApplicationStatic>
6#include <QtCore/QFileInfo>
7#include <QtCore/QJsonArray>
8#include <QtCore/QJsonParseError>
9#include <QtCore/QObject>
11#include <QtCore/QTranslator>
20QString jsonValueTypeToString(QJsonValue::Type type)
24 QJsonValue::Type type;
28 static constexpr const TypeToString typeToStringMap[] = {
29 {QJsonValue::Null,
"NULL"}, {QJsonValue::Bool,
"Bool"}, {QJsonValue::Double,
"Double"},
30 {QJsonValue::String,
"String"}, {QJsonValue::Array,
"Array"}, {QJsonValue::Object,
"Object"},
31 {QJsonValue::Undefined,
"Undefined"},
34 for (
const TypeToString& entry : typeToStringMap) {
35 if (type == entry.type) {
40 return QObject::tr(
"Unknown type: %1").arg(type);
51 for (
const QString& key : keys) {
52 if (!jsonObject.contains(key)) {
53 if (!missingKeys.isEmpty()) {
54 missingKeys += QStringLiteral(
", ");
60 if (!missingKeys.isEmpty()) {
61 errorString = QObject::tr(
"The following required keys are missing: %1").arg(missingKeys);
68bool validateKeyTypes(
const QJsonObject& jsonObject,
const QStringList& keys,
const QList<QJsonValue::Type>& types,
71 if (keys.count() != types.count()) {
72 errorString = QObject::tr(
"Mismatched key and type list sizes: keys=%1 types=%2")
73 .arg(keys.count()).arg(types.count());
77 for (qsizetype i = 0; i < types.count(); i++) {
78 const QString& valueKey = keys[i];
79 if (jsonObject.contains(valueKey)) {
80 const QJsonValue& jsonValue = jsonObject[valueKey];
81 if (types[i] == QJsonValue::Undefined) {
85 if ((jsonValue.type() == QJsonValue::Double) && (types[i] == QJsonValue::Null)) {
89 if (jsonValue.type() != types[i]) {
90 errorString = QObject::tr(
"Incorrect value type - key:type:expected %1:%2:%3")
91 .arg(valueKey, jsonValueTypeToString(jsonValue.type()),
92 jsonValueTypeToString(types[i]));
103 QJsonParseError parseError;
106 if (parseError.error == QJsonParseError::NoError) {
112 qCDebug(JsonParsingLog) <<
"Json read error (compressed input) offset" << parseError.offset;
114 const int startPos = qMax(0, parseError.offset - 100);
115 const int length = qMin(bytes.length() - startPos, 200);
116 qCDebug(JsonParsingLog) <<
"Json read error" << bytes.mid(startPos, length).constData();
118 errorString = QStringLiteral(
"%1 (offset %2)").arg(parseError.errorString()).arg(parseError.offset);
126 if (jsonBytes.isEmpty() && !
errorString.isEmpty()) {
136 if (value.type() == QJsonValue::Null) {
137 return std::numeric_limits<double>::quiet_NaN();
140 return value.toDouble();
146 QList<QJsonValue::Type> typeList;
150 keyList.append(info.key);
159 keyList.append(info.key);
160 typeList.append(info.type);
172 QSet<QString> expectedKeys;
173 expectedKeys.reserve(keyInfo.size());
175 expectedKeys.insert(QLatin1String(info.key));
178 for (
const QString &key : jsonObject.keys()) {
179 if (!expectedKeys.contains(key)) {
180 errorString = QObject::tr(
"Unknown key: %1").arg(key);
196constexpr const char *_translateKeysKey =
"translateKeys";
197constexpr const char *_arrayIDKeysKey =
"_arrayIDKeys";
198constexpr const char *_jsonGroundStationKey =
"groundStation";
199constexpr const char *_jsonGroundStationValue =
"QGroundControl";
203QJsonObject translateObject(QJsonObject &jsonObject,
const QString &translateContext,
const QStringList &translateKeys);
205QJsonArray translateArray(QJsonArray &jsonArray,
const QString &translateContext,
const QStringList &translateKeys)
207 for (qsizetype i = 0; i < jsonArray.count(); i++) {
208 QJsonObject childJsonObject = jsonArray[i].toObject();
209 jsonArray[i] = translateObject(childJsonObject, translateContext, translateKeys);
214QJsonObject translateObject(QJsonObject &jsonObject,
const QString &translateContext,
const QStringList &translateKeys)
216 for (
const QString &key : jsonObject.keys()) {
217 if (jsonObject[key].isString()) {
218 QString locString = jsonObject[key].toString();
219 if (!translateKeys.contains(key)) {
223 QString disambiguation;
224 const QString disambiguationPrefix(
"#loc.disambiguation#");
225 if (locString.startsWith(disambiguationPrefix)) {
226 locString = locString.right(locString.length() - disambiguationPrefix.length());
227 const int commentEndIndex = locString.indexOf(
"#");
228 if (commentEndIndex != -1) {
229 disambiguation = locString.left(commentEndIndex);
230 locString = locString.right(locString.length() - disambiguation.length() - 1);
235 translateContext.toUtf8().constData(),
236 locString.toUtf8().constData(),
237 disambiguation.toUtf8().constData());
238 if (!xlatString.isNull()) {
239 jsonObject[key] = xlatString;
241 }
else if (jsonObject[key].isArray()) {
242 QJsonArray childJsonArray = jsonObject[key].toArray();
243 jsonObject[key] = translateArray(childJsonArray, translateContext, translateKeys);
244 }
else if (jsonObject[key].isObject()) {
245 QJsonObject childJsonObject = jsonObject[key].toObject();
246 jsonObject[key] = translateObject(childJsonObject, translateContext, translateKeys);
255QStringList resolveTranslateKeys(QJsonObject &jsonObject,
256 const QStringList &defaultTranslateKeys,
257 const QStringList &defaultArrayIDKeys)
259 QString translateKeys;
260 if (jsonObject.contains(_translateKeysKey)) {
261 translateKeys = jsonObject[_translateKeysKey].toString();
262 }
else if (!defaultTranslateKeys.isEmpty()) {
263 translateKeys = defaultTranslateKeys.join(
",");
264 jsonObject[_translateKeysKey] = translateKeys;
267 if (!jsonObject.contains(_arrayIDKeysKey) && !defaultArrayIDKeys.isEmpty()) {
268 jsonObject[_arrayIDKeysKey] = defaultArrayIDKeys.join(
",");
271 if (translateKeys.isEmpty()) {
274 return translateKeys.split(
",");
283 return s_jsonTranslator();
288 jsonObject[_jsonGroundStationKey] = _jsonGroundStationValue;
294 int minSupportedVersion,
int maxSupportedVersion,
int &version,
297 static const QList<KeyValidateInfo> requiredKeys = {
307 if (fileTypeValue != expectedFileType) {
308 errorString = QObject::tr(
"Incorrect file type key expected:%1 actual:%2").arg(expectedFileType, fileTypeValue);
313 if (version < minSupportedVersion) {
314 errorString = QObject::tr(
"File version %1 is no longer supported").arg(version);
318 if (version > maxSupportedVersion) {
319 errorString = QObject::tr(
"File version %1 is newer than current supported version %2")
321 .arg(maxSupportedVersion);
329 int minSupportedVersion,
int maxSupportedVersion,
int &version,
332 static const QList<KeyValidateInfo> requiredKeys = {
333 {_jsonGroundStationKey, QJsonValue::String,
true},
345 int minSupportedVersion,
int maxSupportedVersion,
int &version,
347 const QStringList &defaultTranslateKeys,
348 const QStringList &defaultArrayIDKeys)
356 if (!doc.isObject()) {
357 errorString = QObject::tr(
"Root of json file is not object: %1").arg(jsonFilename);
361 QJsonObject jsonObject = doc.object();
369 const QStringList translateKeys = resolveTranslateKeys(jsonObject, defaultTranslateKeys, defaultArrayIDKeys);
370 const QString context = QFileInfo(jsonFilename).fileName();
371 return translateObject(jsonObject, context, translateKeys);
Q_APPLICATION_STATIC(ADSBVehicleManager, _adsbVehicleManager, SettingsManager::instance() ->adsbVehicleManagerSettings())
#define QGC_LOGGING_CATEGORY(name, categoryStr)
bool validateExternalQGCJsonFile(const QJsonObject &jsonObject, const QString &expectedFileType, int minSupportedVersion, int maxSupportedVersion, int &version, QString &errorString)
bool validateKeyTypes(const QJsonObject &jsonObject, const QStringList &keys, const QList< QJsonValue::Type > &types, QString &errorString)
bool validateInternalQGCJsonFile(const QJsonObject &jsonObject, const QString &expectedFileType, int minSupportedVersion, int maxSupportedVersion, int &version, QString &errorString)
bool validateKeys(const QJsonObject &jsonObject, const QList< KeyValidateInfo > &keyInfo, QString &errorString)
Validates that all required keys are present and that listed keys have the expected type.
bool isJsonFile(const QByteArray &bytes, QJsonDocument &jsonDoc, QString &errorString)
Determines whether an in-memory byte buffer contains parseable JSON content.
constexpr const char * jsonFileTypeKey
QJsonObject openInternalQGCJsonFile(const QString &jsonFilename, const QString &expectedFileType, int minSupportedVersion, int maxSupportedVersion, int &version, QString &errorString, const QStringList &defaultTranslateKeys, const QStringList &defaultArrayIDKeys)
double possibleNaNJsonValue(const QJsonValue &value)
Returns NaN if the value is null, or the value converted to double otherwise.
void saveQGCJsonFileHeader(QJsonObject &jsonObject, const QString &fileType, int version)
Saves the standard QGC file header (groundStation, fileType, version) into the json object.
bool validateKeysStrict(const QJsonObject &jsonObject, const QList< KeyValidateInfo > &keyInfo, QString &errorString)
Validates keys like validateKeys but also rejects any keys not listed in keyInfo.
QTranslator * translator()
Translator used by openInternalQGCJsonFile for localized strings.
constexpr const char * jsonVersionKey
bool validateRequiredKeys(const QJsonObject &jsonObject, const QStringList &keys, QString &errorString)
Validates that all listed keys are present in the object.
bool looksLikeCompressedData(const QByteArray &data)
QByteArray readFile(const QString &filePath, QString *errorString, qint64 maxBytes)
Read file contents, transparently decompressing .gz/.xz/.zst/.bz2/.lz4 files.
QJsonDocument parseCompressedJson(const QByteArray &data, QJsonParseError *error)
Parse JSON from data that may be compressed. Auto-detects gzip/xz/zstd/bzip2/lz4.