/* Copyright (C) 2014 * swift project Community / Contributors * * This file is part of swift project. It is subject to the license terms in the LICENSE file found in the top-level * directory of this distribution and at http://www.swift-project.org/license.html. No part of swift project, * including this file, may be copied, modified, propagated, or distributed except according to the terms * contained in the LICENSE file. */ //! \file #ifndef BLACKMISC_JSON_H #define BLACKMISC_JSON_H #include "blackmisc/blackmiscexport.h" #include "blackmisc/fileutils.h" #include "blackmisc/inheritancetraits.h" #include "blackmisc/metaclass.h" #include "blackmisc/jsonexception.h" #include #include #include #include #include #include #include #include #include #include class QDateTime; class QPixmap; class QStringList; namespace BlackMisc { /*! * Simple literal type containing a single QLatin1String. * * Just useful for encapsulating a QLatin1String in a way that inhibits implicit conversion to QString * to avoid ambiguities in overload resolution. */ struct CExplicitLatin1String { //! Embedded string. const QLatin1String m_latin1; //! Implicit constructor. Q_DECL_CONSTEXPR CExplicitLatin1String(QLatin1String s) : m_latin1(s) {} //! Implicit conversion. Q_DECL_CONSTEXPR operator QLatin1String() const { return m_latin1; } }; } /*! * \defgroup JSON Streaming operators for JSON */ //! \name Streaming operators for QJsonValue (to value) //! \ingroup JSON //! @{ BLACKMISC_EXPORT const QJsonValue &operator >>(const QJsonValue &json, int &value); BLACKMISC_EXPORT const QJsonValue &operator >>(const QJsonValue &json, qlonglong &value); BLACKMISC_EXPORT const QJsonValue &operator >>(const QJsonValue &json, qulonglong &value); BLACKMISC_EXPORT const QJsonValue &operator >>(const QJsonValue &json, uint &value); BLACKMISC_EXPORT const QJsonValue &operator >>(const QJsonValue &json, qint16 &value); BLACKMISC_EXPORT const QJsonValue &operator >>(const QJsonValue &json, QString &value); BLACKMISC_EXPORT const QJsonValue &operator >>(const QJsonValue &json, QStringList &value); BLACKMISC_EXPORT const QJsonValue &operator >>(const QJsonValue &json, double &value); BLACKMISC_EXPORT const QJsonValue &operator >>(const QJsonValue &json, bool &value); BLACKMISC_EXPORT const QJsonValue &operator >>(const QJsonValue &json, QDateTime &value); BLACKMISC_EXPORT const QJsonValue &operator >>(const QJsonValue &json, QPixmap &value); BLACKMISC_EXPORT const QJsonValue &operator >>(const QJsonValue &json, QByteArray &value); BLACKMISC_EXPORT const QJsonValueRef &operator >>(const QJsonValueRef &json, int &value); BLACKMISC_EXPORT const QJsonValueRef &operator >>(const QJsonValueRef &json, qlonglong &value); BLACKMISC_EXPORT const QJsonValueRef &operator >>(const QJsonValueRef &json, qulonglong &value); BLACKMISC_EXPORT const QJsonValueRef &operator >>(const QJsonValueRef &json, uint &value); BLACKMISC_EXPORT const QJsonValueRef &operator >>(const QJsonValueRef &json, qint16 &value); BLACKMISC_EXPORT const QJsonValueRef &operator >>(const QJsonValueRef &json, QString &value); BLACKMISC_EXPORT const QJsonValueRef &operator >>(const QJsonValueRef &json, QStringList &value); BLACKMISC_EXPORT const QJsonValueRef &operator >>(const QJsonValueRef &json, double &value); BLACKMISC_EXPORT const QJsonValueRef &operator >>(const QJsonValueRef &json, bool &value); BLACKMISC_EXPORT const QJsonValueRef &operator >>(const QJsonValueRef &json, QDateTime &value); BLACKMISC_EXPORT const QJsonValueRef &operator >>(const QJsonValueRef &json, QPixmap &value); BLACKMISC_EXPORT const QJsonValueRef &operator >>(const QJsonValueRef &json, QByteArray &value); //! @} //! \brief Specialized JSON serialization for enum //! \remarks needs to be in global namespace //! \ingroup JSON //! @{ template std::enable_if_t::value, QJsonObject> &operator<<(QJsonObject &json, std::pair value) { json.insert(value.first, QJsonValue(static_cast(value.second))); return json; } template std::enable_if_t::value, QJsonObject> &operator<<(QJsonObject &json, std::pair value) { json[value.first] = QJsonValue(static_cast(value.second)); return json; } //! @} //! \brief Specialized JSON serialization for QFlags generated enum //! \ingroup JSON //! @{ template QJsonObject &operator<<(QJsonObject &json, std::pair &> value) { json.insert(value.first, QJsonValue(static_cast(value.second))); return json; } template QJsonObject &operator<<(QJsonObject &json, std::pair &> value) { json[value.first] = QJsonValue(static_cast(value.second)); return json; } //! @} //! \brief Specialized JSON deserialization for enum //! \ingroup JSON template std::enable_if_t::value, QJsonValue> const &operator>>(const QJsonValue &json, ENUM &value) { value = static_cast(json.toInt()); return json; } //! \brief Specialized JSON deserialization for QFlags enum //! \ingroup JSON template const QJsonValue &operator>>(const QJsonValue &json, QFlags &value) { value = static_cast>(json.toInt()); return json; } //! \brief Specialized JSON deserialization for enum //! \ingroup JSON template std::enable_if_t::value, QJsonValueRef> const &operator>>(const QJsonValueRef &json, ENUM &value) { value = static_cast(json.toInt()); return json; } //! \brief Specialized JSON deserialization for QFlags enum //! \ingroup JSON template const QJsonValueRef &operator>>(const QJsonValueRef &json, QFlags &value) { value = static_cast>(json.toInt()); return json; } //! \brief Specialized JSON deserialization for pair //! \ingroup JSON template const QJsonValueRef &operator>>(const QJsonValueRef &json, std::pair &pair) { json.toArray() >> pair.first >> pair.second; return json; } //! \brief Specialized JSON serialization for pair //! \ingroup JSON template QJsonArray &operator<<(QJsonArray &json, const std::pair &pair) { QJsonArray array; return json << QJsonValue(array << pair.first << pair.second); } //! \name Streaming operators for QJsonArray (from value) //! \ingroup JSON //! @{ BLACKMISC_EXPORT QJsonArray &operator<<(QJsonArray &json, const int value); BLACKMISC_EXPORT QJsonArray &operator<<(QJsonArray &json, const std::pair &value); BLACKMISC_EXPORT QJsonArray &operator<<(QJsonArray &json, const qlonglong value); BLACKMISC_EXPORT QJsonArray &operator<<(QJsonArray &json, const uint value); BLACKMISC_EXPORT QJsonArray &operator<<(QJsonArray &json, const qulonglong value); BLACKMISC_EXPORT QJsonArray &operator<<(QJsonArray &json, const QString &value); BLACKMISC_EXPORT QJsonArray &operator<<(QJsonArray &json, const double value); BLACKMISC_EXPORT QJsonArray &operator<<(QJsonArray &json, const bool value); BLACKMISC_EXPORT QJsonArray &operator<<(QJsonArray &json, const QDateTime &value); BLACKMISC_EXPORT QJsonArray &operator<<(QJsonArray &json, const QPixmap &value); BLACKMISC_EXPORT QJsonArray &operator<<(QJsonArray &json, const QByteArray &value); //! @} //! \name Streaming operators for QJsonObject (from value) //! \ingroup JSON //! @{ BLACKMISC_EXPORT QJsonObject &operator<<(QJsonObject &json, const std::pair &value); BLACKMISC_EXPORT QJsonObject &operator<<(QJsonObject &json, const std::pair &value); BLACKMISC_EXPORT QJsonObject &operator<<(QJsonObject &json, const std::pair &value); BLACKMISC_EXPORT QJsonObject &operator<<(QJsonObject &json, const std::pair &value); BLACKMISC_EXPORT QJsonObject &operator<<(QJsonObject &json, const std::pair &value); BLACKMISC_EXPORT QJsonObject &operator<<(QJsonObject &json, const std::pair &value); BLACKMISC_EXPORT QJsonObject &operator<<(QJsonObject &json, const std::pair &value); BLACKMISC_EXPORT QJsonObject &operator<<(QJsonObject &json, const std::pair &value); BLACKMISC_EXPORT QJsonObject &operator<<(QJsonObject &json, const std::pair &value); BLACKMISC_EXPORT QJsonObject &operator<<(QJsonObject &json, const std::pair &value); BLACKMISC_EXPORT QJsonObject &operator<<(QJsonObject &json, const std::pair &value); BLACKMISC_EXPORT QJsonObject &operator<<(QJsonObject &json, const std::pair &value); BLACKMISC_EXPORT QJsonObject &operator<<(QJsonObject &json, const std::pair &value); BLACKMISC_EXPORT QJsonObject &operator<<(QJsonObject &json, const std::pair &value); BLACKMISC_EXPORT QJsonObject &operator<<(QJsonObject &json, const std::pair &value); BLACKMISC_EXPORT QJsonObject &operator<<(QJsonObject &json, const std::pair &value); BLACKMISC_EXPORT QJsonObject &operator<<(QJsonObject &json, const std::pair &value); BLACKMISC_EXPORT QJsonObject &operator<<(QJsonObject &json, const std::pair &value); BLACKMISC_EXPORT QJsonObject &operator<<(QJsonObject &json, const std::pair &value); BLACKMISC_EXPORT QJsonObject &operator<<(QJsonObject &json, const std::pair &value); BLACKMISC_EXPORT QJsonObject &operator<<(QJsonObject &json, const std::pair &value); BLACKMISC_EXPORT QJsonObject &operator<<(QJsonObject &json, const std::pair &value); BLACKMISC_EXPORT QJsonObject &operator<<(QJsonObject &json, const std::pair &value); BLACKMISC_EXPORT QJsonObject &operator<<(QJsonObject &json, const std::pair &value); //! @} namespace BlackMisc { class CEmpty; namespace Json { //! Append to first JSON object (concatenate) //! \ingroup JSON BLACKMISC_EXPORT QJsonObject &appendJsonObject(QJsonObject &target, const QJsonObject &toBeAppended); //! JSON Object from string //! \ingroup JSON BLACKMISC_EXPORT QJsonObject jsonObjectFromString(const QString &json); //! JSON Array from string //! \ingroup JSON BLACKMISC_EXPORT QJsonArray jsonArrayFromString(const QString &json); //! Creates an incremental json object from two existing objects BLACKMISC_EXPORT QJsonObject getIncrementalObject(const QJsonObject &previousObject, const QJsonObject ¤tObject); //! Merges an incremental json object into an existing one BLACKMISC_EXPORT QJsonObject applyIncrementalObject(const QJsonObject &previousObject, const QJsonObject &incrementalObject); //! Looks like swift JSON? //! \remark Quick check if the string could be a valid JSON string BLACKMISC_EXPORT bool looksLikeJson(const QString &json); //! Looks like swift JSON? //! \remark Quick check if the string could be a valid swift JSON string BLACKMISC_EXPORT bool looksLikeSwiftJson(const QString &json); /*! * Load JSON file and init by that */ template bool loadFromJsonFile(T &object, const QString &fileNameAndPath) { const QString jsonString(CFileUtils::readFileToString(fileNameAndPath)); if (jsonString.isEmpty()) { return false; } object.convertFromJson(jsonString); return true; } /*! * Save to JSON file */ template bool saveToJsonFile(const T &object, const QString &fileNameAndPath) { const QString jsonString(object.toJsonString()); if (jsonString.isEmpty()) { return false; } return CFileUtils::writeStringToFile(jsonString, fileNameAndPath); } } // Json namespace Mixin { /*! * CRTP class template which will generate marshalling operators for a derived class with its own marshalling implementation. * * \tparam Must implement public methods QJsonObject toJson() const and void convertFromJson(const QJsonObject &json). */ template class JsonOperators { public: //! operator >> for JSON friend const QJsonObject &operator>>(const QJsonObject &json, Derived &obj) { obj.convertFromJson(json); return json; } //! operator >> for JSON friend const QJsonValue &operator>>(const QJsonValue &json, Derived &obj) { obj.convertFromJson(json.toObject()); return json; } //! operator >> for JSON friend const QJsonValueRef &operator>>(const QJsonValueRef &json, Derived &obj) { obj.convertFromJson(json.toObject()); return json; } //! operator << for JSON friend QJsonArray &operator<<(QJsonArray &json, const Derived &obj) { json.append(obj.toJson()); return json; } //! operator << for JSON friend QJsonObject &operator<<(QJsonObject &json, const std::pair &value) { json.insert(value.first, QJsonValue(value.second.toJson())); return json; } //! operator << for JSON friend QJsonObject &operator<<(QJsonObject &json, const std::pair &value) { json[value.first] = QJsonValue(value.second.toJson()); return json; } }; /*! * CRTP class template from which a derived class can inherit common methods dealing with JSON by metatuple. * * \see BLACKMISC_DECLARE_USING_MIXIN_JSON */ template class JsonByMetaClass : public JsonOperators { public: //! Cast to JSON object QJsonObject toJson() const { QJsonObject json; auto meta = introspect().without(MetaFlags()); meta.forEachMember([ &, this ](auto member) { json << std::make_pair(CExplicitLatin1String(member.latin1Name()), std::cref(member.in(*this->derived()))); }); return Json::appendJsonObject(json, baseToJson(static_cast *>(derived()))); } //! Convenience function JSON as string QString toJsonString(QJsonDocument::JsonFormat format = QJsonDocument::Indented) const { QJsonDocument jsonDoc(toJson()); return jsonDoc.toJson(format); } //! Assign from JSON object void convertFromJson(const QJsonObject &json) { baseConvertFromJson(static_cast *>(derived()), json); auto meta = introspect().without(MetaFlags()); meta.forEachMember([ &, this ](auto member) { const auto value = json.value(CExplicitLatin1String(member.latin1Name())); if (value.isUndefined()) { constexpr bool required = false; //! \fixme add RequiredForJson flag in metaclass system if (required) { throw CJsonException(QStringLiteral("Missing required member '%1'").arg(member.latin1Name())); } } else { CJsonScope scope(member.latin1Name()); value >> member.in(*this->derived()); } }); } //! Assign from JSON object string void convertFromJson(const QString &jsonString) { convertFromJson(BlackMisc::Json::jsonObjectFromString(jsonString)); } //! Get object from QJsonObject template static DerivedObj fromJson(const QJsonObject &json) { DerivedObj obj; obj.convertFromJson(json); return obj; } //! Get object from JSON string template static DerivedObj fromJson(const QString &jsonString) { DerivedObj obj; obj.convertFromJson(BlackMisc::Json::jsonObjectFromString(jsonString)); return obj; } private: const Derived *derived() const { return static_cast(this); } Derived *derived() { return static_cast(this); } template static QJsonObject baseToJson(const T *base) { return base->toJson(); } template static void baseConvertFromJson(T *base, const QJsonObject &json) { base->convertFromJson(json); } static QJsonObject baseToJson(const void *) { return {}; } static void baseConvertFromJson(void *, const QJsonObject &) {} static QJsonObject baseToJson(const CEmpty *) { return {}; } static void baseConvertFromJson(CEmpty *, const QJsonObject &) {} }; /*! * When a derived class and a base class both inherit from Mixin::JsonByTuple, * the derived class uses this macro to disambiguate the inherited members. */ #define BLACKMISC_DECLARE_USING_MIXIN_JSON(DERIVED) \ using ::BlackMisc::Mixin::JsonByMetaClass::toJson; \ using ::BlackMisc::Mixin::JsonByMetaClass::convertFromJson; } // Mixin } // guard #endif // guard