// Licensed under the Apache-2.0 license. See README.md for details. #pragma once #include #include #include #include #include #include #include #include #include #include "Exception.h" namespace Json { DECLARE_EXCEPTION(Json); enum Requirement { Required }; void write(const QJsonDocument &doc, const QString &filename); void write(const QJsonObject &object, const QString &filename); void write(const QJsonArray &array, const QString &filename); QByteArray toBinary(const QJsonObject &obj); QByteArray toBinary(const QJsonArray &array); QByteArray toText(const QJsonObject &obj); QByteArray toText(const QJsonArray &array); QJsonDocument ensureDocument(const QByteArray &data, const QString &what = "Document"); QJsonDocument ensureDocument(const QString &filename, const QString &what = "Document"); QJsonObject ensureObject(const QJsonDocument &doc, const QString &what = "Document"); QJsonArray ensureArray(const QJsonDocument &doc, const QString &what = "Document"); /////////////////// WRITING //////////////////// void writeString(QJsonObject & to, const QString &key, const QString &value); void writeStringList(QJsonObject & to, const QString &key, const QStringList &values); template void writeObjectList(QJsonObject & to, QString key, QList> values) { if (!values.isEmpty()) { QJsonArray array; for (auto value: values) { array.append(value->toJson()); } to.insert(key, array); } } template void writeObjectList(QJsonObject & to, QString key, QList values) { if (!values.isEmpty()) { QJsonArray array; for (auto value: values) { array.append(value.toJson()); } to.insert(key, array); } } template QJsonValue toJson(const T &t) { return QJsonValue(t); } template<> QJsonValue toJson(const QUrl &url); template<> QJsonValue toJson(const QByteArray &data); template<> QJsonValue toJson(const QDateTime &datetime); template<> QJsonValue toJson(const QDir &dir); template<> QJsonValue toJson(const QUuid &uuid); template<> QJsonValue toJson(const QVariant &variant); template QJsonArray toJsonArray(const QList &container) { QJsonArray array; for (const T item : container) { array.append(toJson(item)); } return array; } ////////////////// READING //////////////////// template T ensureIsType(const QJsonValue &value, const Requirement requirement = Required, const QString &what = "Value"); template<> double ensureIsType(const QJsonValue &value, const Requirement, const QString &what); template<> bool ensureIsType(const QJsonValue &value, const Requirement, const QString &what); template<> int ensureIsType(const QJsonValue &value, const Requirement, const QString &what); template<> QJsonObject ensureIsType(const QJsonValue &value, const Requirement, const QString &what); template<> QJsonArray ensureIsType(const QJsonValue &value, const Requirement, const QString &what); template<> QJsonValue ensureIsType(const QJsonValue &value, const Requirement, const QString &what); template<> QByteArray ensureIsType(const QJsonValue &value, const Requirement, const QString &what); template<> QDateTime ensureIsType(const QJsonValue &value, const Requirement, const QString &what); template<> QVariant ensureIsType(const QJsonValue &value, const Requirement, const QString &what); template<> QString ensureIsType(const QJsonValue &value, const Requirement, const QString &what); template<> QUuid ensureIsType(const QJsonValue &value, const Requirement, const QString &what); template<> QDir ensureIsType(const QJsonValue &value, const Requirement, const QString &what); template<> QUrl ensureIsType(const QJsonValue &value, const Requirement, const QString &what); // the following functions are higher level functions, that make use of the above functions for // type conversion template T ensureIsType(const QJsonValue &value, const T default_, const QString &what = "Value") { if (value.isUndefined()) { return default_; } return ensureIsType(value, Required, what); } template T ensureIsType(const QJsonObject &parent, const QString &key, const Requirement requirement = Required, const QString &what = "__placeholder__") { const QString localWhat = QString(what).replace("__placeholder__", '\'' + key + '\''); if (!parent.contains(key)) { throw JsonException(localWhat + "s parent does not contain " + localWhat); } return ensureIsType(parent.value(key), requirement, localWhat); } template T ensureIsType(const QJsonObject &parent, const QString &key, const T default_, const QString &what = "__placeholder__") { const QString localWhat = QString(what).replace("__placeholder__", '\'' + key + '\''); if (!parent.contains(key)) { return default_; } return ensureIsType(parent.value(key), default_, localWhat); } template QList ensureIsArrayOf(const QJsonDocument &doc) { const QJsonArray array = ensureArray(doc); QList out; for (const QJsonValue val : array) { out.append(ensureIsType(val, Required, "Document")); } return out; } template QList ensureIsArrayOf(const QJsonValue &value, const Requirement = Required, const QString &what = "Value") { const QJsonArray array = ensureIsType(value, Required, what); QList out; for (const QJsonValue val : array) { out.append(ensureIsType(val, Required, what)); } return out; } template QList ensureIsArrayOf(const QJsonValue &value, const QList default_, const QString &what = "Value") { if (value.isUndefined()) { return default_; } return ensureIsArrayOf(value, Required, what); } template QList ensureIsArrayOf(const QJsonObject &parent, const QString &key, const Requirement requirement = Required, const QString &what = "__placeholder__") { const QString localWhat = QString(what).replace("__placeholder__", '\'' + key + '\''); if (!parent.contains(key)) { throw JsonException(localWhat + "s parent does not contain " + localWhat); } return ensureIsArrayOf(parent.value(key), requirement, localWhat); } template QList ensureIsArrayOf(const QJsonObject &parent, const QString &key, const QList &default_, const QString &what = "__placeholder__") { const QString localWhat = QString(what).replace("__placeholder__", '\'' + key + '\''); if (!parent.contains(key)) { return default_; } return ensureIsArrayOf(parent.value(key), default_, localWhat); } // this macro part could be replaced by variadic functions that just pass on their arguments, but that wouldn't work well with IDE helpers #define JSON_HELPERFUNCTIONS(NAME, TYPE) \ inline TYPE ensure##NAME(const QJsonValue &value, const Requirement requirement = Required, const QString &what = "Value") \ { return ensureIsType(value, requirement, what); } \ inline TYPE ensure##NAME(const QJsonValue &value, const TYPE default_, const QString &what = "Value") \ { return ensureIsType(value, default_, what); } \ inline TYPE ensure##NAME(const QJsonObject &parent, const QString &key, const Requirement requirement = Required, const QString &what = "__placeholder__") \ { return ensureIsType(parent, key, requirement, what); } \ inline TYPE ensure##NAME(const QJsonObject &parent, const QString &key, const TYPE default_, const QString &what = "__placeholder") \ { return ensureIsType(parent, key, default_, what); } JSON_HELPERFUNCTIONS(Array, QJsonArray) JSON_HELPERFUNCTIONS(Object, QJsonObject) JSON_HELPERFUNCTIONS(JsonValue, QJsonValue) JSON_HELPERFUNCTIONS(String, QString) JSON_HELPERFUNCTIONS(Boolean, bool) JSON_HELPERFUNCTIONS(Double, double) JSON_HELPERFUNCTIONS(Integer, int) JSON_HELPERFUNCTIONS(DateTime, QDateTime) JSON_HELPERFUNCTIONS(Url, QUrl) JSON_HELPERFUNCTIONS(ByteArray, QByteArray) JSON_HELPERFUNCTIONS(Dir, QDir) JSON_HELPERFUNCTIONS(Uuid, QUuid) JSON_HELPERFUNCTIONS(Variant, QVariant) #undef JSON_HELPERFUNCTIONS } using JSONValidationError = Json::JsonException;