#pragma once #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace LtsUtils { class JsonParseError : public std::runtime_error { public: JsonParseError(const QJsonParseError &err) : std::runtime_error( "json parse error: " + err.errorString().toStdString() + " - at offset " + std::to_string(err.offset)) {} }; class JsonError : public std::runtime_error { public: using std::runtime_error::runtime_error; }; class JsonKeyError : public JsonError { public: using JsonError::JsonError; }; class JsonRangeError : public JsonError { public: using JsonError::JsonError; }; class JsonTypeError : public JsonError { public: using JsonError::JsonError; }; class JsonObject { public: class Key; JsonObject() = default; explicit JsonObject(const QJsonObject &json) : m_json(json), m_jsonIsSet(true) {} explicit JsonObject(QJsonObject &&json) : m_json(std::move(json)), m_jsonIsSet(true) {} static inline JsonObject fromJson(const char *json, int len = -1); static inline JsonObject fromJson(const QByteArray &json) { return fromJson(json.data(), json.size()); } static inline JsonObject fromJson(const std::string &json) { return fromJson(json.c_str(), json.size()); } static inline JsonObject fromJsonSafe(const char *json, int len = -1) noexcept(false); static inline JsonObject fromJsonSafe(const QByteArray &json) noexcept(false) { return fromJsonSafe(json.data(), json.size()); } static inline JsonObject fromJsonSafe(const std::string &json) noexcept(false) { return fromJsonSafe(json.c_str(), json.size()); } template [[nodiscard]] bool contains(const Key &key) const; [[nodiscard]] bool isNull(const Key &key) const { return contains(key); } [[nodiscard]] bool isEmpty() const { return m_json.isEmpty(); } [[nodiscard]] bool isValid() const { return m_jsonIsSet; } template T value(const Key &key) const noexcept(false); template T valueOr(const Key &key, const T &defaultValue) const noexcept(false); template [[nodiscard]] bool isMap() const; template QMap toMap() const noexcept(false); template std::map toStdMap() const; bool operator==(const JsonObject &other) const { return m_json == other.m_json; } bool operator!=(const JsonObject &other) const { return !(*this == other); } class Key { public: Key(QString s) : m_s(std::move(s)) {} Key(const std::string &s) : m_s(QString::fromStdString(s)) {} Key(const char *s) : m_s(QString::fromUtf8(s)) {} operator const QString &() const { return m_s; } private: const QString m_s; }; operator const QJsonObject &() const { return m_json; } [[nodiscard]] const QJsonObject &object() const { return m_json; } private: [[nodiscard]] bool containsAny(const QString &key) const { return m_json.contains(key); } QJsonObject m_json; bool m_jsonIsSet = false; }; JsonObject JsonObject::fromJson(const char *json, int len) { if (len < 0) len = strlen(json); QJsonParseError error; const auto doc = QJsonDocument::fromJson(QByteArray::fromRawData(json, len), &error); if (error.error != QJsonParseError::NoError) return {}; if (!doc.isObject()) return {}; return JsonObject(doc.object()); } JsonObject JsonObject::fromJsonSafe(const char *json, int len) noexcept(false) { if (len < 0) len = strlen(json); QJsonParseError error; const auto doc = QJsonDocument::fromJson(QByteArray::fromRawData(json, len), &error); if (error.error != QJsonParseError::NoError) throw JsonParseError(error); if (!doc.isObject()) throw JsonTypeError("JSON root is not an object"); return JsonObject(doc.object()); } class JsonArray { public: JsonArray() = default; explicit JsonArray(const QJsonArray &json) : m_json(json), m_jsonIsSet(true) {} explicit JsonArray(QJsonArray &&json) : m_json(std::move(json)), m_jsonIsSet(true) {} static inline JsonArray fromJson(const char *json, int len = -1); static inline JsonArray fromJson(const QByteArray &json) { return fromJson(json.data(), json.size()); } static inline JsonArray fromJson(const std::string &json) { return fromJson(json.c_str(), json.size()); } static inline JsonArray fromJsonSafe(const char *json, int len = -1) noexcept(false); static inline JsonArray fromJsonSafe(const QByteArray &json) noexcept(false) { return fromJson(json.data(), json.size()); } static inline JsonArray fromJsonSafe(const std::string &json) noexcept(false) { return fromJson(json.c_str(), json.size()); } [[nodiscard]] int size() const { return m_json.size(); } [[nodiscard]] bool isEmpty() const { return m_json.isEmpty(); } [[nodiscard]] bool isValid() const { return m_jsonIsSet; } template [[nodiscard]] bool contains(int index) const noexcept(false); [[nodiscard]] bool isNull(int index) const noexcept(false) { return contains(index); } template T at(int index) const noexcept(false); template [[nodiscard]] bool isVector() const; template QVector toVector() const noexcept(false); template std::vector toStdVector() const; bool operator==(const JsonArray &other) const { return m_json == other.m_json; } bool operator!=(const JsonArray &other) const { return !(*this == other); } const QJsonArray &operator()() const { return m_json; } private: QJsonArray m_json; bool m_jsonIsSet = false; }; JsonArray JsonArray::fromJson(const char *json, int len) { if (len < 0) len = strlen(json); QJsonParseError error; QJsonDocument doc = QJsonDocument::fromJson(QByteArray::fromRawData(json, len), &error); if (error.error != QJsonParseError::NoError) return {}; if (!doc.isArray()) return {}; return JsonArray(doc.array()); } JsonArray JsonArray::fromJsonSafe(const char *json, int len) noexcept(false) { if (len < 0) len = strlen(json); QJsonParseError error; const auto doc = QJsonDocument::fromJson(QByteArray::fromRawData(json, len), &error); if (error.error != QJsonParseError::NoError) throw JsonParseError(error); if (!doc.isArray()) throw JsonTypeError("JSON root is not an array"); return JsonArray(doc.array()); } template struct JsonValue; template struct JsonLimits { static constexpr Int lowest() { return std::numeric_limits::lowest(); } static constexpr Int max() { return std::numeric_limits::max(); } }; template <> struct JsonLimits { static constexpr int64_t lowest() { return -(static_cast(1) << 53) + 1; } static constexpr int64_t max() { return (static_cast(1) << 53) - 1; } }; template <> struct JsonLimits { static constexpr uint64_t lowest() { return 0; } static constexpr uint64_t max() { return JsonLimits::max(); } }; template struct JsonIntValue { static bool matchesType(const QJsonValue &value) { if (!value.isDouble()) return false; double val = value.toDouble(); if (val < JsonLimits::lowest()) return false; if (val > JsonLimits::max()) return false; double wholeInt = static_cast(val); return val == wholeInt; } static Int value(const QJsonValue &value) noexcept(false) { if (!matchesType(value)) throw JsonTypeError("JSON value not of type Int"); return static_cast(value.toDouble()); } }; template <> struct JsonValue : JsonIntValue {}; template <> struct JsonValue : JsonIntValue {}; template <> struct JsonValue : JsonIntValue {}; template <> struct JsonValue : JsonIntValue {}; template <> struct JsonValue : JsonIntValue {}; template <> struct JsonValue : JsonIntValue {}; template <> struct JsonValue { static bool matchesType(const QJsonValue &value) { return value.isDouble(); } static double value(const QJsonValue &value) noexcept(false) { if (!matchesType(value)) throw JsonTypeError("JSON value not of type Double"); return value.toDouble(); } }; template <> struct JsonValue { static bool matchesType(const QJsonValue &value) { return value.isBool(); } static bool value(const QJsonValue &value) noexcept(false) { if (!matchesType(value)) throw JsonTypeError("JSON value not of type Bool"); return value.toBool(); } }; template <> struct JsonValue { static bool matchesType(const QJsonValue &value) { return value.isString(); } static QString value(const QJsonValue &value) noexcept(false) { if (!matchesType(value)) throw JsonTypeError("JSON value not of type String"); return value.toString(); } }; template <> struct JsonValue { static bool matchesType(const QJsonValue &value) { return value.isString(); } static std::string value(const QJsonValue &value) { return JsonValue::value(value).toStdString(); } }; template <> struct JsonValue { static bool matchesType(const QJsonValue &value) { return value.isObject(); } static QJsonObject value(const QJsonValue &value) noexcept(false) { if (!matchesType(value)) throw JsonTypeError("JSON value not of type Object"); return value.toObject(); } }; template <> struct JsonValue { static bool matchesType(const QJsonValue &value) { return value.isObject(); } static JsonObject value(const QJsonValue &value) { return JsonObject(JsonValue::value(value)); } }; template <> struct JsonValue { static bool matchesType(const QJsonValue &value) { return value.isNull(); } }; template <> struct JsonValue { static bool matchesType(const QJsonValue &value) { return value.isArray(); } static QJsonArray value(const QJsonValue &value) noexcept(false) { if (!matchesType(value)) throw JsonTypeError("JSON value not of type Array"); return value.toArray(); } }; template <> struct JsonValue { static bool matchesType(const QJsonValue &value) { return value.isArray(); } static JsonArray value(const QJsonValue &value) { return JsonArray(JsonValue::value(value)); } }; template struct JsonValue> { static bool matchesType(const QJsonValue &value) { if (!value.isArray()) return false; const JsonArray array(value.toArray()); return array.isVector(); } static QVector value(const QJsonValue &value) { const JsonArray array(JsonValue::value(value)); return array.toVector(); } }; template struct JsonValue> { static bool matchesType(const QJsonValue &value) { return JsonValue>::matchesType(value); } static std::vector value(const QJsonValue &value) { const JsonArray array(JsonValue::value(value)); return array.toStdVector(); } }; template struct JsonValue> { static bool matchesType(const QJsonValue &value) { if (!value.isObject()) return false; const JsonObject obj(JsonValue::value(value)); return obj.isMap(); } static QMap value(const QJsonValue &value) { const JsonObject obj(JsonValue::value(value)); return obj.toMap(); } }; template struct JsonValue> { static bool matchesType(const QJsonValue &value) { return JsonValue>::matchesType(value); } static std::map value(const QJsonValue &value) { const JsonObject obj(JsonValue::value(value)); return obj.toStdMap(); } }; template bool JsonObject::contains(const Key &key) const { if (!m_json.contains(key)) return false; return JsonValue::matchesType(m_json.value(key)); } template T JsonObject::value(const Key &key) const noexcept(false) { if (!containsAny(key)) throw JsonKeyError("no element with key: " + static_cast(key).toStdString()); try { return JsonValue::value(m_json.value(key)); } catch (const JsonTypeError &typeError) { throw JsonTypeError(std::string(typeError.what()) + " - key: " + static_cast(key).toStdString()); } } template T JsonObject::valueOr(const Key &key, const T &defaultValue) const { try { return value(key); } catch (const JsonKeyError &) { return defaultValue; } } template bool JsonObject::isMap() const { const QStringList keys = m_json.keys(); for (const auto &key : keys) if (!JsonValue::matchesType(m_json.value(key))) return false; return true; } template QMap JsonObject::toMap() const { QMap result; for (auto current = m_json.begin(); current != m_json.end(); ++current) result.insert(current.key(), JsonValue::value(current.value())); return result; } template std::map JsonObject::toStdMap() const { std::map result; for (auto current = m_json.begin(); current != m_json.end(); ++current) result.insert( {current.key().toStdString(), JsonValue::value(current.value())}); return result; } template bool JsonArray::contains(int index) const noexcept(false) { if (index < 0 || index >= m_json.size()) throw JsonRangeError("index out of range: " + std::to_string(index)); return JsonValue::matchesType(m_json.at(index)); } template T JsonArray::at(int index) const noexcept(false) { if (index < 0 || index >= m_json.size()) throw JsonRangeError("index out of range: " + std::to_string(index)); return JsonValue::value(m_json.at(index)); } template bool JsonArray::isVector() const { for (const QJsonValue &value : m_json) if (!JsonValue::matchesType(value)) return false; return true; } template QVector JsonArray::toVector() const { QVector result; result.reserve(m_json.size()); for (const QJsonValue &value : m_json) result.push_back(JsonValue::value(value)); return result; } template std::vector JsonArray::toStdVector() const { std::vector result; result.reserve(m_json.size()); for (const QJsonValue &value : m_json) result.push_back(JsonValue::value(value)); return result; } } // namespace LtsUtils