Files
qt6-migration-pipeline/Lts/Common/LtsUtils/LtsUtilsCore/json_object.h
T

520 lines
15 KiB
C++

#pragma once
#include <QJsonArray>
#include <QJsonDocument>
#include <QJsonObject>
#include <QJsonParseError>
#include <QJsonValue>
#include <QMap>
#include <QString>
#include <QVector>
#include <cstdint>
#include <exception>
#include <limits>
#include <map>
#include <string>
#include <utility>
#include <vector>
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 <class T> [[nodiscard]] bool contains(const Key &key) const;
[[nodiscard]] bool isNull(const Key &key) const {
return contains<void>(key);
}
[[nodiscard]] bool isEmpty() const { return m_json.isEmpty(); }
[[nodiscard]] bool isValid() const { return m_jsonIsSet; }
template <class T> T value(const Key &key) const noexcept(false);
template <class T>
T valueOr(const Key &key, const T &defaultValue) const noexcept(false);
template <class T> [[nodiscard]] bool isMap() const;
template <class T> QMap<QString, T> toMap() const noexcept(false);
template <class T> std::map<std::string, T> 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 <class T>
[[nodiscard]] bool contains(int index) const noexcept(false);
[[nodiscard]] bool isNull(int index) const noexcept(false) {
return contains<void>(index);
}
template <class T> T at(int index) const noexcept(false);
template <class T> [[nodiscard]] bool isVector() const;
template <class T> QVector<T> toVector() const noexcept(false);
template <class T> std::vector<T> 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 <class T> struct JsonValue;
template <class Int> struct JsonLimits {
static constexpr Int lowest() { return std::numeric_limits<Int>::lowest(); }
static constexpr Int max() { return std::numeric_limits<Int>::max(); }
};
template <> struct JsonLimits<int64_t> {
static constexpr int64_t lowest() {
return -(static_cast<int64_t>(1) << 53) + 1;
}
static constexpr int64_t max() { return (static_cast<int64_t>(1) << 53) - 1; }
};
template <> struct JsonLimits<uint64_t> {
static constexpr uint64_t lowest() { return 0; }
static constexpr uint64_t max() { return JsonLimits<int64_t>::max(); }
};
template <class Int> struct JsonIntValue {
static bool matchesType(const QJsonValue &value) {
if (!value.isDouble())
return false;
double val = value.toDouble();
if (val < JsonLimits<Int>::lowest())
return false;
if (val > JsonLimits<Int>::max())
return false;
double wholeInt = static_cast<Int>(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<Int>(value.toDouble());
}
};
template <> struct JsonValue<int16_t> : JsonIntValue<int16_t> {};
template <> struct JsonValue<uint16_t> : JsonIntValue<uint16_t> {};
template <> struct JsonValue<int32_t> : JsonIntValue<int32_t> {};
template <> struct JsonValue<uint32_t> : JsonIntValue<uint32_t> {};
template <> struct JsonValue<int64_t> : JsonIntValue<int64_t> {};
template <> struct JsonValue<uint64_t> : JsonIntValue<uint64_t> {};
template <> struct JsonValue<double> {
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<bool> {
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<QString> {
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<std::string> {
static bool matchesType(const QJsonValue &value) { return value.isString(); }
static std::string value(const QJsonValue &value) {
return JsonValue<QString>::value(value).toStdString();
}
};
template <> struct JsonValue<QJsonObject> {
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<JsonObject> {
static bool matchesType(const QJsonValue &value) { return value.isObject(); }
static JsonObject value(const QJsonValue &value) {
return JsonObject(JsonValue<QJsonObject>::value(value));
}
};
template <> struct JsonValue<void> {
static bool matchesType(const QJsonValue &value) { return value.isNull(); }
};
template <> struct JsonValue<QJsonArray> {
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<JsonArray> {
static bool matchesType(const QJsonValue &value) { return value.isArray(); }
static JsonArray value(const QJsonValue &value) {
return JsonArray(JsonValue<QJsonArray>::value(value));
}
};
template <class T> struct JsonValue<QVector<T>> {
static bool matchesType(const QJsonValue &value) {
if (!value.isArray())
return false;
const JsonArray array(value.toArray());
return array.isVector<T>();
}
static QVector<T> value(const QJsonValue &value) {
const JsonArray array(JsonValue<QJsonArray>::value(value));
return array.toVector<T>();
}
};
template <class T> struct JsonValue<std::vector<T>> {
static bool matchesType(const QJsonValue &value) {
return JsonValue<QVector<T>>::matchesType(value);
}
static std::vector<T> value(const QJsonValue &value) {
const JsonArray array(JsonValue<QJsonArray>::value(value));
return array.toStdVector<T>();
}
};
template <class T> struct JsonValue<QMap<QString, T>> {
static bool matchesType(const QJsonValue &value) {
if (!value.isObject())
return false;
const JsonObject obj(JsonValue<QJsonObject>::value(value));
return obj.isMap<T>();
}
static QMap<QString, T> value(const QJsonValue &value) {
const JsonObject obj(JsonValue<QJsonObject>::value(value));
return obj.toMap<T>();
}
};
template <class T> struct JsonValue<std::map<std::string, T>> {
static bool matchesType(const QJsonValue &value) {
return JsonValue<QMap<QString, T>>::matchesType(value);
}
static std::map<std::string, T> value(const QJsonValue &value) {
const JsonObject obj(JsonValue<QJsonObject>::value(value));
return obj.toStdMap<T>();
}
};
template <class T> bool JsonObject::contains(const Key &key) const {
if (!m_json.contains(key))
return false;
return JsonValue<T>::matchesType(m_json.value(key));
}
template <class T> T JsonObject::value(const Key &key) const noexcept(false) {
if (!containsAny(key))
throw JsonKeyError("no element with key: " +
static_cast<const QString &>(key).toStdString());
try {
return JsonValue<T>::value(m_json.value(key));
} catch (const JsonTypeError &typeError) {
throw JsonTypeError(std::string(typeError.what()) + " - key: " +
static_cast<const QString &>(key).toStdString());
}
}
template <class T>
T JsonObject::valueOr(const Key &key, const T &defaultValue) const {
try {
return value<T>(key);
}
catch (const JsonKeyError &) {
return defaultValue;
}
}
template <class T> bool JsonObject::isMap() const {
const QStringList keys = m_json.keys();
for (const auto &key : keys)
if (!JsonValue<T>::matchesType(m_json.value(key)))
return false;
return true;
}
template <class T> QMap<QString, T> JsonObject::toMap() const {
QMap<QString, T> result;
for (auto current = m_json.begin(); current != m_json.end(); ++current)
result.insert(current.key(), JsonValue<T>::value(current.value()));
return result;
}
template <class T> std::map<std::string, T> JsonObject::toStdMap() const {
std::map<std::string, T> result;
for (auto current = m_json.begin(); current != m_json.end(); ++current)
result.insert(
{current.key().toStdString(), JsonValue<T>::value(current.value())});
return result;
}
template <class T> 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<T>::matchesType(m_json.at(index));
}
template <class T> 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<T>::value(m_json.at(index));
}
template <class T> bool JsonArray::isVector() const {
for (const QJsonValue &value : m_json)
if (!JsonValue<T>::matchesType(value))
return false;
return true;
}
template <class T> QVector<T> JsonArray::toVector() const {
QVector<T> result;
result.reserve(m_json.size());
for (const QJsonValue &value : m_json)
result.push_back(JsonValue<T>::value(value));
return result;
}
template <class T> std::vector<T> JsonArray::toStdVector() const {
std::vector<T> result;
result.reserve(m_json.size());
for (const QJsonValue &value : m_json)
result.push_back(JsonValue<T>::value(value));
return result;
}
} // namespace LtsUtils