aboutsummaryrefslogtreecommitdiff
path: root/lib/converters.h
diff options
context:
space:
mode:
authorKitsune Ral <Kitsune-Ral@users.sf.net>2018-12-08 15:36:04 +0900
committerKitsune Ral <Kitsune-Ral@users.sf.net>2018-12-08 20:12:22 +0900
commit3392e66fd015e191b01f6e3fc6839edc3948e31f (patch)
treec839259aece7462d978f7aa9eeb712edd932cc98 /lib/converters.h
parent95d4df58b39962f771885a6615efe1a682aab356 (diff)
downloadlibquotient-3392e66fd015e191b01f6e3fc6839edc3948e31f.tar.gz
libquotient-3392e66fd015e191b01f6e3fc6839edc3948e31f.zip
Refactor toJson/fillJson
Both now use through a common JsonConverter<> template class with its base definition tuned for structs/QJsonObjects and specialisations for non-object types. This new implementation doesn't work with virtual fillJson functions yet (so EventContent classes still use toJson as a member function) and does not cope quite well with non-constructible objects (you have to specialise JsonConverter<> rather than, more intuitively, JsonObjectConverter<>), but overall is more streamlined compared to the previous implementation. It also fixes one important issue that pushed for a rewrite: the previous implementation was not working with structure hierarchies at all so (in particular) the Filter part of CS API was totally disfunctional.
Diffstat (limited to 'lib/converters.h')
-rw-r--r--lib/converters.h288
1 files changed, 138 insertions, 150 deletions
diff --git a/lib/converters.h b/lib/converters.h
index 53855a1f..6227902d 100644
--- a/lib/converters.h
+++ b/lib/converters.h
@@ -57,238 +57,221 @@ class QVariant;
namespace QMatrixClient
{
- // This catches anything implicitly convertible to QJsonValue/Object/Array
- inline auto toJson(const QJsonValue& val) { return val; }
- inline auto toJson(const QJsonObject& o) { return o; }
- inline auto toJson(const QJsonArray& arr) { return arr; }
- // Special-case QString to avoid ambiguity between QJsonValue
- // and QVariant (also, QString.isEmpty() is used in _impl::AddNode<> below)
- inline auto toJson(const QString& s) { return s; }
-
- inline QJsonArray toJson(const QStringList& strings)
- {
- return QJsonArray::fromStringList(strings);
- }
-
- inline QString toJson(const QByteArray& bytes)
+ template <typename T>
+ struct JsonObjectConverter
{
- return bytes.constData();
- }
+ static void dumpTo(QJsonObject& jo, const T& pod) { jo = pod; }
+ static void fillFrom(const QJsonObject& jo, T& pod) { pod = jo; }
+ };
- // QVariant is outrageously omnivorous - it consumes whatever is not
- // exactly matching the signature of other toJson overloads. The trick
- // below disables implicit conversion to QVariant through its numerous
- // non-explicit constructors.
- QJsonValue variantToJson(const QVariant& v);
template <typename T>
- inline auto toJson(T&& /* const QVariant& or QVariant&& */ var)
- -> std::enable_if_t<std::is_same<std::decay_t<T>, QVariant>::value,
- QJsonValue>
+ struct JsonConverter
{
- return variantToJson(var);
- }
- QJsonObject toJson(const QMap<QString, QVariant>& map);
-#if (QT_VERSION >= QT_VERSION_CHECK(5, 5, 0))
- QJsonObject toJson(const QHash<QString, QVariant>& hMap);
-#endif
+ static QJsonObject dump(const T& pod)
+ {
+ QJsonObject jo;
+ JsonObjectConverter<T>::dumpTo(jo, pod);
+ return jo;
+ }
+ static T doLoad(const QJsonObject& jo)
+ {
+ T pod;
+ JsonObjectConverter<T>::fillFrom(jo, pod);
+ return pod;
+ }
+ static T load(const QJsonValue& jv) { return doLoad(jv.toObject()); }
+ static T load(const QJsonDocument& jd) { return doLoad(jd.object()); }
+ };
template <typename T>
- inline QJsonArray toJson(const std::vector<T>& vals)
+ inline auto toJson(const T& pod)
{
- QJsonArray ar;
- for (const auto& v: vals)
- ar.push_back(toJson(v));
- return ar;
+ return JsonConverter<T>::dump(pod);
}
template <typename T>
- inline QJsonArray toJson(const QVector<T>& vals)
+ inline auto fillJson(QJsonObject& json, const T& data)
{
- QJsonArray ar;
- for (const auto& v: vals)
- ar.push_back(toJson(v));
- return ar;
+ JsonObjectConverter<T>::dumpTo(json, data);
}
template <typename T>
- inline QJsonObject toJson(const QSet<T>& set)
+ inline auto fromJson(const QJsonValue& jv)
{
- QJsonObject json;
- for (auto e: set)
- json.insert(toJson(e), QJsonObject{});
- return json;
+ return JsonConverter<T>::load(jv);
}
template <typename T>
- inline QJsonObject toJson(const QHash<QString, T>& hashMap)
+ inline T fromJson(const QJsonDocument& jd)
{
- QJsonObject json;
- for (auto it = hashMap.begin(); it != hashMap.end(); ++it)
- json.insert(it.key(), toJson(it.value()));
- return json;
+ return JsonConverter<T>::load(jd);
}
template <typename T>
- inline QJsonObject toJson(const std::unordered_map<QString, T>& hashMap)
+ inline void fromJson(const QJsonValue& jv, T& pod)
{
- QJsonObject json;
- for (auto it = hashMap.begin(); it != hashMap.end(); ++it)
- json.insert(it.key(), toJson(it.value()));
- return json;
+ pod = fromJson<T>(jv);
}
template <typename T>
- struct FromJsonObject
+ inline void fromJson(const QJsonDocument& jd, T& pod)
{
- T operator()(const QJsonObject& jo) const { return T(jo); }
- };
+ pod = fromJson<T>(jd);
+ }
+ // Unfolds Omittable<>
template <typename T>
- struct FromJson
+ inline void fromJson(const QJsonValue& jv, Omittable<T>& pod)
{
- T operator()(const QJsonValue& jv) const
- {
- return FromJsonObject<T>()(jv.toObject());
- }
- T operator()(const QJsonDocument& jd) const
- {
- return FromJsonObject<T>()(jd.object());
- }
- };
+ pod = fromJson<T>(jv);
+ }
template <typename T>
- inline auto fromJson(const QJsonValue& jv)
+ inline void fillFromJson(const QJsonValue& jv, T& pod)
{
- return FromJson<T>()(jv);
+ JsonObjectConverter<T>::fillFrom(jv.toObject(), pod);
}
+ // JsonConverter<> specialisations
+
template <typename T>
- inline auto fromJson(const QJsonDocument& jd)
+ struct TrivialJsonDumper
{
- return FromJson<T>()(jd);
- }
+ // Works for: QJsonValue (and all things it can consume),
+ // QJsonObject, QJsonArray
+ static auto dump(const T& val) { return val; }
+ };
- template <> struct FromJson<bool>
+ template <> struct JsonConverter<bool> : public TrivialJsonDumper<bool>
{
- auto operator()(const QJsonValue& jv) const { return jv.toBool(); }
+ static auto load(const QJsonValue& jv) { return jv.toBool(); }
};
- template <> struct FromJson<int>
+ template <> struct JsonConverter<int> : public TrivialJsonDumper<int>
{
- auto operator()(const QJsonValue& jv) const { return jv.toInt(); }
+ static auto load(const QJsonValue& jv) { return jv.toInt(); }
};
- template <> struct FromJson<double>
+ template <> struct JsonConverter<double>
+ : public TrivialJsonDumper<double>
{
- auto operator()(const QJsonValue& jv) const { return jv.toDouble(); }
+ static auto load(const QJsonValue& jv) { return jv.toDouble(); }
};
- template <> struct FromJson<float>
+ template <> struct JsonConverter<float> : public TrivialJsonDumper<float>
{
- auto operator()(const QJsonValue& jv) const { return float(jv.toDouble()); }
+ static auto load(const QJsonValue& jv) { return float(jv.toDouble()); }
};
- template <> struct FromJson<qint64>
+ template <> struct JsonConverter<qint64>
+ : public TrivialJsonDumper<qint64>
{
- auto operator()(const QJsonValue& jv) const { return qint64(jv.toDouble()); }
+ static auto load(const QJsonValue& jv) { return qint64(jv.toDouble()); }
};
- template <> struct FromJson<QString>
+ template <> struct JsonConverter<QString>
+ : public TrivialJsonDumper<QString>
{
- auto operator()(const QJsonValue& jv) const { return jv.toString(); }
+ static auto load(const QJsonValue& jv) { return jv.toString(); }
};
- template <> struct FromJson<QDateTime>
+ template <> struct JsonConverter<QDateTime>
{
- auto operator()(const QJsonValue& jv) const
+ static auto dump(const QDateTime& val) = delete; // not provided yet
+ static auto load(const QJsonValue& jv)
{
- return QDateTime::fromMSecsSinceEpoch(fromJson<qint64>(jv), Qt::UTC);
+ return QDateTime::fromMSecsSinceEpoch(
+ fromJson<qint64>(jv), Qt::UTC);
}
};
- template <> struct FromJson<QDate>
+ template <> struct JsonConverter<QDate>
{
- auto operator()(const QJsonValue& jv) const
+ static auto dump(const QDate& val) = delete; // not provided yet
+ static auto load(const QJsonValue& jv)
{
return fromJson<QDateTime>(jv).date();
}
};
- template <> struct FromJson<QJsonArray>
+ template <> struct JsonConverter<QJsonArray>
+ : public TrivialJsonDumper<QJsonArray>
{
- auto operator()(const QJsonValue& jv) const
- {
- return jv.toArray();
- }
+ static auto load(const QJsonValue& jv) { return jv.toArray(); }
};
- template <> struct FromJson<QByteArray>
+ template <> struct JsonConverter<QByteArray>
{
- auto operator()(const QJsonValue& jv) const
+ static QString dump(const QByteArray& ba) { return ba.constData(); }
+ static auto load(const QJsonValue& jv)
{
return fromJson<QString>(jv).toLatin1();
}
};
- template <> struct FromJson<QVariant>
+ template <> struct JsonConverter<QVariant>
{
- QVariant operator()(const QJsonValue& jv) const;
+ static QJsonValue dump(const QVariant& v);
+ static QVariant load(const QJsonValue& jv);
};
- template <typename VectorT>
- struct ArrayFromJson
+ template <typename VectorT,
+ typename T = typename VectorT::value_type>
+ struct JsonArrayConverter
{
- auto operator()(const QJsonArray& ja) const
+ static void dumpTo(QJsonArray& ar, const VectorT& vals)
{
- using size_type = typename VectorT::size_type;
- VectorT vect; vect.resize(size_type(ja.size()));
- std::transform(ja.begin(), ja.end(),
- vect.begin(), FromJson<typename VectorT::value_type>());
- return vect;
+ for (const auto& v: vals)
+ ar.push_back(toJson(v));
}
- auto operator()(const QJsonValue& jv) const
+ static auto dump(const VectorT& vals)
{
- return operator()(jv.toArray());
+ QJsonArray ja;
+ dumpTo(ja, vals);
+ return ja;
}
- auto operator()(const QJsonDocument& jd) const
+ static auto load(const QJsonArray& ja)
{
- return operator()(jd.array());
+ VectorT vect; vect.reserve(typename VectorT::size_type(ja.size()));
+ for (const auto& i: ja)
+ vect.push_back(fromJson<T>(i));
+ return vect;
}
+ static auto load(const QJsonValue& jv) { return load(jv.toArray()); }
+ static auto load(const QJsonDocument& jd) { return load(jd.array()); }
};
- template <typename T>
- struct FromJson<std::vector<T>> : ArrayFromJson<std::vector<T>>
+ template <typename T> struct JsonConverter<std::vector<T>>
+ : public JsonArrayConverter<std::vector<T>>
{ };
- template <typename T>
- struct FromJson<QVector<T>> : ArrayFromJson<QVector<T>>
+ template <typename T> struct JsonConverter<QVector<T>>
+ : public JsonArrayConverter<QVector<T>>
+ { };
+
+ template <typename T> struct JsonConverter<QList<T>>
+ : public JsonArrayConverter<QList<T>>
{ };
- template <typename T> struct FromJson<QList<T>>
+ template <> struct JsonConverter<QStringList>
+ : public JsonConverter<QList<QString>>
{
- auto operator()(const QJsonValue& jv) const
+ static auto dump(const QStringList& sl)
{
- const auto jsonArray = jv.toArray();
- QList<T> sl; sl.reserve(jsonArray.size());
- std::transform(jsonArray.begin(), jsonArray.end(),
- std::back_inserter(sl), FromJson<T>());
- return sl;
+ return QJsonArray::fromStringList(sl);
}
};
- template <> struct FromJson<QStringList> : FromJson<QList<QString>> { };
-
- template <> struct FromJson<QMap<QString, QVariant>>
+ template <> struct JsonObjectConverter<QSet<QString>>
{
- QMap<QString, QVariant> operator()(const QJsonValue& jv) const;
- };
-
- template <typename T> struct FromJson<QSet<T>>
- {
- auto operator()(const QJsonValue& jv) const
+ static void dumpTo(QJsonObject& json, const QSet<QString>& s)
+ {
+ for (const auto& e: s)
+ json.insert(toJson(e), QJsonObject{});
+ }
+ static auto fillFrom(const QJsonObject& json, QSet<QString>& s)
{
- const auto json = jv.toObject();
- QSet<T> s; s.reserve(json.size());
+ s.reserve(s.size() + json.size());
for (auto it = json.begin(); it != json.end(); ++it)
s.insert(it.key());
return s;
@@ -298,39 +281,44 @@ namespace QMatrixClient
template <typename HashMapT>
struct HashMapFromJson
{
- auto operator()(const QJsonObject& jo) const
+ static void dumpTo(QJsonObject& json, const HashMapT& hashMap)
+ {
+ for (auto it = hashMap.begin(); it != hashMap.end(); ++it)
+ json.insert(it.key(), toJson(it.value()));
+ }
+ static void fillFrom(const QJsonObject& jo, HashMapT& h)
{
- HashMapT h; h.reserve(jo.size());
+ h.reserve(jo.size());
for (auto it = jo.begin(); it != jo.end(); ++it)
h[it.key()] =
fromJson<typename HashMapT::mapped_type>(it.value());
- return h;
- }
- auto operator()(const QJsonValue& jv) const
- {
- return operator()(jv.toObject());
- }
- auto operator()(const QJsonDocument& jd) const
- {
- return operator()(jd.object());
}
};
template <typename T>
- struct FromJson<std::unordered_map<QString, T>>
- : HashMapFromJson<std::unordered_map<QString, T>>
+ struct JsonObjectConverter<std::unordered_map<QString, T>>
+ : public HashMapFromJson<std::unordered_map<QString, T>>
{ };
template <typename T>
- struct FromJson<QHash<QString, T>> : HashMapFromJson<QHash<QString, T>>
+ struct JsonObjectConverter<QHash<QString, T>>
+ : public HashMapFromJson<QHash<QString, T>>
{ };
+ // We could use std::conditional<> below but QT_VERSION* macros in C++ code
+ // cause (kinda valid but useless and noisy) compiler warnings about
+ // bitwise operations on signed integers; so use the preprocessor for now.
+ using variant_map_t =
#if (QT_VERSION >= QT_VERSION_CHECK(5, 5, 0))
- template <> struct FromJson<QHash<QString, QVariant>>
+ QVariantHash;
+#else
+ QVariantMap;
+#endif
+ template <> struct JsonConverter<variant_map_t>
{
- QHash<QString, QVariant> operator()(const QJsonValue& jv) const;
+ static QJsonObject dump(const variant_map_t& vh);
+ static QVariantHash load(const QJsonValue& jv);
};
-#endif
// Conditional insertion into a QJsonObject