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 ((jsonValue.type() == QJsonValue::Double) && (types[i] == QJsonValue::Null)) {
85 if (jsonValue.type() != types[i]) {
86 errorString = QObject::tr(
"Incorrect value type - key:type:expected %1:%2:%3")
87 .arg(valueKey, jsonValueTypeToString(jsonValue.type()),
88 jsonValueTypeToString(types[i]));
99 QJsonParseError parseError;
102 if (parseError.error == QJsonParseError::NoError) {
106 const int startPos = qMax(0, parseError.offset - 100);
107 const int length = qMin(bytes.length() - startPos, 200);
108 qCDebug(JsonParsingLog) <<
"Json read error" << bytes.mid(startPos, length).constData();
117 if (jsonBytes.isEmpty() && !
errorString.isEmpty()) {
126 if (value.type() == QJsonValue::Null) {
127 return std::numeric_limits<double>::quiet_NaN();
130 return value.toDouble();
136 QList<QJsonValue::Type> typeList;
140 keyList.append(info.key);
149 keyList.append(info.key);
150 typeList.append(info.type);
162 QSet<QString> expectedKeys;
163 expectedKeys.reserve(keyInfo.size());
165 expectedKeys.insert(QLatin1String(info.key));
168 for (
const QString &key : jsonObject.keys()) {
169 if (!expectedKeys.contains(key)) {
170 errorString = QStringLiteral(
"Unknown key: %1").arg(key);
186constexpr const char *_translateKeysKey =
"translateKeys";
187constexpr const char *_arrayIDKeysKey =
"_arrayIDKeys";
188constexpr const char *_jsonGroundStationKey =
"groundStation";
189constexpr const char *_jsonGroundStationValue =
"QGroundControl";
193QJsonObject translateObject(QJsonObject &jsonObject,
const QString &translateContext,
const QStringList &translateKeys);
195QJsonArray translateArray(QJsonArray &jsonArray,
const QString &translateContext,
const QStringList &translateKeys)
197 for (qsizetype i = 0; i < jsonArray.count(); i++) {
198 QJsonObject childJsonObject = jsonArray[i].toObject();
199 jsonArray[i] = translateObject(childJsonObject, translateContext, translateKeys);
204QJsonObject translateObject(QJsonObject &jsonObject,
const QString &translateContext,
const QStringList &translateKeys)
206 for (
const QString &key : jsonObject.keys()) {
207 if (jsonObject[key].isString()) {
208 QString locString = jsonObject[key].toString();
209 if (!translateKeys.contains(key)) {
213 QString disambiguation;
214 const QString disambiguationPrefix(
"#loc.disambiguation#");
215 if (locString.startsWith(disambiguationPrefix)) {
216 locString = locString.right(locString.length() - disambiguationPrefix.length());
217 const int commentEndIndex = locString.indexOf(
"#");
218 if (commentEndIndex != -1) {
219 disambiguation = locString.left(commentEndIndex);
220 locString = locString.right(locString.length() - disambiguation.length() - 1);
225 translateContext.toUtf8().constData(),
226 locString.toUtf8().constData(),
227 disambiguation.toUtf8().constData());
228 if (!xlatString.isNull()) {
229 jsonObject[key] = xlatString;
231 }
else if (jsonObject[key].isArray()) {
232 QJsonArray childJsonArray = jsonObject[key].toArray();
233 jsonObject[key] = translateArray(childJsonArray, translateContext, translateKeys);
234 }
else if (jsonObject[key].isObject()) {
235 QJsonObject childJsonObject = jsonObject[key].toObject();
236 jsonObject[key] = translateObject(childJsonObject, translateContext, translateKeys);
245QStringList resolveTranslateKeys(QJsonObject &jsonObject,
246 const QStringList &defaultTranslateKeys,
247 const QStringList &defaultArrayIDKeys)
249 QString translateKeys;
250 if (jsonObject.contains(_translateKeysKey)) {
251 translateKeys = jsonObject[_translateKeysKey].toString();
252 }
else if (!defaultTranslateKeys.isEmpty()) {
253 translateKeys = defaultTranslateKeys.join(
",");
254 jsonObject[_translateKeysKey] = translateKeys;
257 if (!jsonObject.contains(_arrayIDKeysKey) && !defaultArrayIDKeys.isEmpty()) {
258 jsonObject[_arrayIDKeysKey] = defaultArrayIDKeys.join(
",");
261 if (translateKeys.isEmpty()) {
264 return translateKeys.split(
",");
273 return s_jsonTranslator();
278 jsonObject[_jsonGroundStationKey] = _jsonGroundStationValue;
284 int minSupportedVersion,
int maxSupportedVersion,
int &version,
287 static const QList<KeyValidateInfo> requiredKeys = {
297 if (fileTypeValue != expectedFileType) {
298 errorString = QObject::tr(
"Incorrect file type key expected:%1 actual:%2").arg(expectedFileType, fileTypeValue);
303 if (version < minSupportedVersion) {
304 errorString = QObject::tr(
"File version %1 is no longer supported").arg(version);
308 if (version > maxSupportedVersion) {
309 errorString = QObject::tr(
"File version %1 is newer than current supported version %2")
311 .arg(maxSupportedVersion);
319 int minSupportedVersion,
int maxSupportedVersion,
int &version,
322 static const QList<KeyValidateInfo> requiredKeys = {
323 {_jsonGroundStationKey, QJsonValue::String,
true},
335 int minSupportedVersion,
int maxSupportedVersion,
int &version,
337 const QStringList &defaultTranslateKeys,
338 const QStringList &defaultArrayIDKeys)
345 QJsonParseError jsonParseError;
347 if (jsonParseError.error != QJsonParseError::NoError) {
348 errorString = QObject::tr(
"Unable to parse json file: %1 error: %2 offset: %3")
349 .arg(jsonFilename, jsonParseError.errorString())
350 .arg(jsonParseError.offset);
354 if (!doc.isObject()) {
355 errorString = QObject::tr(
"Root of json file is not object: %1").arg(jsonFilename);
359 QJsonObject jsonObject = doc.object();
367 const QStringList translateKeys = resolveTranslateKeys(jsonObject, defaultTranslateKeys, defaultArrayIDKeys);
368 const QString context = QFileInfo(jsonFilename).fileName();
369 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.
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.