Qx v0.5.8
Qt Extensions Library
Loading...
Searching...
No Matches
qx-json.h
Go to the documentation of this file.
1#ifndef QX_JSON_H
2#define QX_JSON_H
3
4// Shared Lib Support
5#include "qx/core/qx_core_export.h"
6
7// Qt Includes
8#include <QString>
9#include <QJsonValueRef>
10#include <QJsonObject>
11#include <QJsonArray>
12#include <QJsonDocument>
13#include <QFile>
14#include <QFileInfo>
15
16// Intra-component Includes
18#include "qx/core/qx-error.h"
19
20// Extra-component Includes
23
24//-Macros------------------------------------------------------------------
26#define __QX_JSON_META_STRUCT_INSIDE(meta_tuple) \
27template <typename StructT> \
28 struct QxJsonMetaStructInside \
29{ \
30 static inline constexpr auto memberMetadata() \
31 { \
32 return meta_tuple; \
33 } \
34}
35
36#define __QX_JSON_META_STRUCT_OUTSIDE(self_type, meta_tuple) \
37namespace QxJson \
38{ \
39 template <typename StructT> \
40 struct QxJsonMetaStructOutside<self_type, StructT> \
41 { \
42 static inline constexpr auto memberMetadata() \
43 { \
44 return meta_tuple; \
45 } \
46 }; \
47}
48
51/* TODO: See if there is a magic way to have QX_JSON_STRUCT_X only require QX_JSON_MEMBER_ALIASED
52 * for each member the user wants to be aliased, but not require QX_JSON_MEMBER for members that
53 * are to have a default name
54 */
55#define QX_JSON_MEMBER(member) QxJsonPrivate::makeMemberMetadata<#member>(&StructT::member)
56#define QX_JSON_MEMBER_ALIASED(member, key) QxJsonPrivate::makeMemberMetadata<key>(&StructT::member)
57
58#define QX_JSON_STRUCT(...) __QX_JSON_META_STRUCT_INSIDE(std::make_tuple(QX_FOR_EACH_DELIM(QX_JSON_MEMBER, __VA_ARGS__)))
59#define QX_JSON_STRUCT_X(...) __QX_JSON_META_STRUCT_INSIDE(std::make_tuple(__VA_ARGS__))
60
61#define QX_JSON_STRUCT_OUTSIDE(Struct, ...) __QX_JSON_META_STRUCT_OUTSIDE(Struct, std::make_tuple(QX_FOR_EACH_DELIM(QX_JSON_MEMBER, __VA_ARGS__)))
62#define QX_JSON_STRUCT_OUTSIDE_X(Struct, ...) __QX_JSON_META_STRUCT_OUTSIDE(Struct, std::make_tuple(__VA_ARGS__))
63
64/*
65 * TODO: Create an "inside" version of this macro and its underlying functions, though it's tricky
66 * given clang/GCCs issues with non-namespace explicit template specializations so either a dummy
67 * template parameter will need to be used (to make it technically a partial specialization) or
68 * the specializations themselves will still need to be outside the struct (second macro), while
69 * the declaration of the inner override struct (first macro, e.g. QX_JSON_DECLARE_MEMBER_OVERRIDES(); )
70 * is inside
71 */
72#define QX_JSON_MEMBER_OVERRIDE(Struct, member, ...) \
73namespace QxJson \
74{ \
75 template<> \
76 struct MemberOverrideCoverter<Struct, #member> \
77 { \
78 __VA_ARGS__\
79 }; \
80}
81
82namespace QxJson
83{
84
85class QX_CORE_EXPORT File
86{
87private:
88 QString mIdentifier;
89 QString mFileError;
90
91public:
92 File(const QString& filename, const QString& fileError = {});
93 File(const QFile& docFile, const QString& fileError = {});
94 File(const QFileInfo& docFile, const QString& fileError = {});
95
96 QString string() const;
97};
98
99class QX_CORE_EXPORT Document
100{
101private:
102 QString mName;
103
104public:
105 Document(const QString& name = {});
106
107 QString string() const;
108};
109
110class QX_CORE_EXPORT Object
111{
112public:
113 Object();
114
115 QString string() const;
116};
117
118class QX_CORE_EXPORT ObjectKey
119{
120private:
121 QString mName;
122
123public:
124 ObjectKey(const QString& name);
125
126 QString string() const;
127};
128
129class QX_CORE_EXPORT Array
130{
131public:
132 Array();
133
134 QString string() const;
135};
136
137class QX_CORE_EXPORT ArrayElement
138{
139private:
140 uint mElement;
141
142public:
143 ArrayElement(uint element);
144
145 QString string() const;
146};
147
148using ContextNode = std::variant<File, Document, Object, ObjectKey, Array, ArrayElement>;
149
150} // namespace QxJson
151
152namespace Qx
153{
154
155class QX_CORE_EXPORT JsonError final : public AbstractError<"Qx::JsonError", 5>
156{
157 //-Class Enums-------------------------------------------------------------
158public:
170
171 //-Class Variables-------------------------------------------------------------
172private:
173 static inline const QHash<Form, QString> ERR_STRINGS{
174 {NoError, u""_s},
175 {MissingKey, u"The key does not exist."_s},
176 {TypeMismatch, u"Value type mismatch."_s},
177 {EmptyDoc, u"The document is empty."_s},
178 {MissingFile, u"File does not exist."_s},
179 {InaccessibleFile, u"Cannot open the file."_s},
180 {FileReadError, u"File read error."_s},
181 {FileWriteError, u"File write error."_s}
182 };
183
184 //-Instance Variables-------------------------------------------------------------
185private:
186 QString mAction;
187 Form mForm;
189
190 //-Constructor-----------------------------------------------------------------
191public:
192 JsonError();
193 JsonError(const QString& a, Form f);
194
195 //-Instance Functions-------------------------------------------------------------
196private:
197 quint32 deriveValue() const override;
198 QString derivePrimary() const override;
199 QString deriveSecondary() const override;
200 QString deriveDetails() const override;
201
202public:
203 bool isValid() const;
204 QString action() const;
205 Form form() const;
206 QList<QxJson::ContextNode> context() const;
207 JsonError& withContext(const QxJson::ContextNode& node);
208};
209
210} // namespace Qx
211
213namespace QxJsonPrivate
214{
215//-Namespace Variables---------------------------------------------------
216static inline const QString ERR_CONV_TYPE = u"JSON Error: Converting value to %1"_s;
217static inline const QString ERR_NO_KEY = u"JSON Error: Could not retrieve key '%1'."_s;
218static inline const QString ERR_PARSE_DOC = u"JSON Error: Could not parse JSON document."_s;
219static inline const QString ERR_READ_FILE = u"JSON Error: Could not read JSON file."_s;
220static inline const QString ERR_WRITE_FILE = u"JSON Error: Could not write JSON file."_s;
221
222//-Structs---------------------------------------------------------------
223template<Qx::StringLiteral MemberN, typename MemberT, class Struct>
224struct MemberMetadata
225{
226 constexpr static Qx::StringLiteral M_NAME = MemberN;
227 typedef MemberT M_TYPE;
228 MemberT Struct::* mPtr;
229};
230
231//-Functions-------------------------------------------------------------
232template <Qx::StringLiteral N, typename T, class S>
233constexpr MemberMetadata<N, T, S> makeMemberMetadata(T S::*memberPtr)
234{
235 return {memberPtr};
236}
237
238template<typename T> [[maybe_unused]] static inline QString typeString() = delete;
239template<typename T> [[maybe_unused]] static inline bool isType(const QJsonValue& v) = delete;
240template<typename T> [[maybe_unused]] static inline T toType(const QJsonValue& v) = delete;
241
242template<> inline QString typeString<bool>() { return u"bool"_s; };
243template<> inline QString typeString<double>() { return u"double"_s; };
244template<> inline QString typeString<QString>() { return u"string"_s; };
245template<> inline QString typeString<QJsonArray>() { return u"array"_s; };
246template<> inline QString typeString<QJsonObject>() { return u"object"_s; };
247
248template<> inline bool isType<bool>(const QJsonValue& v) { return v.isBool(); };
249template<> inline bool isType<double>(const QJsonValue& v) { return v.isDouble(); };
250template<> inline bool isType<QString>(const QJsonValue& v) { return v.isString(); };
251template<> inline bool isType<QJsonArray>(const QJsonValue& v) { return v.isArray(); };
252template<> inline bool isType<QJsonObject>(const QJsonValue& v) { return v.isObject(); };
253
254template<> inline bool toType<bool>(const QJsonValue& v) { return v.toBool(); };
255template<> inline double toType<double>(const QJsonValue& v) { return v.toDouble(); };
256template<> inline QString toType<QString>(const QJsonValue& v) { return v.toString(); };
257template<> inline QJsonArray toType<QJsonArray>(const QJsonValue& v) { return v.toArray(); };
258template<> inline QJsonObject toType<QJsonObject>(const QJsonValue& v) { return v.toObject(); };
259
260} // namespace QxJsonPrivate
263namespace QxJson
264{
265//-Structs---------------------------------------------------------------
266template<typename T>
268
269template<class Struct, Qx::StringLiteral member>
271
272template<typename SelfType, typename DelayedSelfType>
274
275//-Concepts--------------------------------------------------------------
276
277template<typename T>
279
280template<typename T>
281concept json_struct_inside = requires {
282 T::template QxJsonMetaStructInside<T>::memberMetadata();
283};
284
285template<typename T>
286concept json_struct_outside = requires {
287 QxJsonMetaStructOutside<T, T>::memberMetadata();
288};
289
290template<typename T>
293
294template<typename T>
295concept json_convertible = requires(T& tValue) {
296 { Converter<T>::fromJson(tValue, QJsonValue()) } -> std::same_as<Qx::JsonError>;
297 { Converter<T>::toJson(tValue) } -> qjson_type;
298};
299
300template<class K, typename T, Qx::StringLiteral N>
301concept json_override_convertible = requires(T& tValue) {
302 { MemberOverrideCoverter<K, N>::fromJson(tValue, QJsonValue()) } -> std::same_as<Qx::JsonError>;
303 { MemberOverrideCoverter<K, N>::toJson(tValue) } -> qjson_type;
304};
305
306template<typename Key, class Value>
307Key keygen(const Value& value) = delete;
308
309template<typename Key, class Value>
310concept json_keyable = requires(const Value& v) {
311 { keygen<Key, Value>(v) } -> std::same_as<Key>;
312};
313
314template<typename T>
317
318template<typename T>
322
323template<typename T>
326
327template<typename T>
330
331} // namespace QxJson
332
334namespace QxJsonPrivate
335{
336//-Functions-------------------------------------------------------------
337/* These helpers are required as a form on indirection within
338 *
339 * template<typename T>
340 * requires json_struct<T>
341 * struct Converter<T> {...}
342 *
343 * That functions makes/made use of 'if constexpr' statements that contain
344 * references to a type in their true branches that doesn't exist if the
345 * false branches are taken. Different compilers seem to discard the untaken
346 * branch of the value dependent statement at different stages as it compiled
347 * fine with MSVC and a newer version of GCC, but not clang or older GCC versions.
348 * To clarify, compilation would fail due to the type in the not-yet-discarded
349 * branch not being declared.
350 *
351 * Putting the reference of the potentially undeclared types behind these functions
352 * that always exist solves the issue.
353 *
354 * These likely can be avoided with C++23's 'if consteval'.
355 */
356template<class K>
358constexpr auto getMemberMeta()
359{
360 return K::template QxJsonMetaStructInside<K>::memberMetadata();
361}
362
363template<class K>
365constexpr auto getMemberMeta()
366{
368}
369
370template<class K, typename T, Qx::StringLiteral N>
372Qx::JsonError overrideParse(T& value, const QJsonValue& jv)
373{
375}
376
377template<class K, typename T, Qx::StringLiteral N>
379auto overrideSerialize(const T& value)
380{
382}
383
384template<typename T>
386Qx::JsonError standardParse(T& value, const QJsonValue& jv)
387{
388 return QxJson::Converter<T>::fromJson(value, jv);
389}
390
391template<typename T>
393auto standardSerialize(const T& value)
394{
395 return QxJson::Converter<T>::toJson(value);
396}
399} // namespace QxJsonPrivate
400
401
402namespace QxJson
403{
404//-Default Converter Specializations-------------------------------------
406template<typename T>
407 requires qjson_type<T>
408struct Converter<T>
409{
410 static Qx::JsonError fromJson(T& value, const QJsonValue& jValue)
411 {
412 if(!QxJsonPrivate::isType<T>(jValue))
413 return Qx::JsonError(QxJsonPrivate::ERR_CONV_TYPE.arg(QxJsonPrivate::typeString<T>()), Qx::JsonError::TypeMismatch);
414
415 value = QxJsonPrivate::toType<T>(jValue);
416 return Qx::JsonError();
417 }
418
419 static T toJson(const T& value)
420 {
421 /* We could automatically box with QJsonValue here, but it's technically unnecessary as every possible
422 * type returned here is implicitly convertible to QJsonValue. Additionally, doing so would complicate
423 * the json_convertible concept as it would have to allow QJsonValue as well as the underlying
424 * types.
425 */
426 return value; // No-op
427 }
428};
429
430template<typename T>
431 requires json_struct<T>
432struct Converter<T>
433{
434 static Qx::JsonError fromJson(T& value, const QJsonValue& jValue)
435 {
436 if(!jValue.isObject())
437 return Qx::JsonError(QxJsonPrivate::ERR_CONV_TYPE.arg(QxJsonPrivate::typeString<QJsonObject>()), Qx::JsonError::TypeMismatch)
439
440 // Underlying object
441 QJsonObject jObject = jValue.toObject();
442
443 // Error tracker
444 Qx::JsonError cnvError;
445
446 // Get member metadata tuple
447 constexpr auto memberMetas = QxJsonPrivate::getMemberMeta<T>();
448
449 // "Iterate" over each tuple element via std::apply, with a fold expression
450 // utilizing && which short-circuits. This allows us to "break" the loop
451 // upon a member conversion failure as single return of false in the inner-most
452 // lambda will trigger the short-circuit.
453 std::apply([&](auto&&... memberMeta) constexpr {
454 // Fold expression
455 ([&]{
456 // Meta
457 static constexpr auto mName = std::remove_reference<decltype(memberMeta)>::type::M_NAME;
458 constexpr QLatin1StringView mKey(mName.value);
459 using mType = typename std::remove_reference<decltype(memberMeta)>::type::M_TYPE;
460 auto& mRef = value.*(memberMeta.mPtr);
461
462 // Get value from key
463 if(!jObject.contains(mKey))
464 {
465 if constexpr(json_optional<mType>)
466 {
467 mRef = std::nullopt;
468 return true;
469 }
470 else
471 {
472 cnvError = Qx::JsonError(QxJsonPrivate::ERR_NO_KEY.arg(mKey), Qx::JsonError::MissingKey)
474 return false;
475 }
476 }
477 QJsonValue mValue = jObject.value(mKey);
478
479 // Convert value
480 if constexpr(json_override_convertible<T, mType, mName>)
481 cnvError = QxJsonPrivate::overrideParse<T, mType, mName>(mRef, mValue);
482 else
483 cnvError = QxJsonPrivate::standardParse<mType>(mRef, mValue);
484
486 return !cnvError.isValid();
487 }() && ...);
488 }, memberMetas);
489
490 return cnvError;
491 }
492
493 static QJsonObject toJson(const T& value)
494 {
495 // Object to fill
496 QJsonObject jObject;
497
498 // Get member metadata tuple
499 constexpr auto memberMetas = QxJsonPrivate::getMemberMeta<T>();
500
501 // "Iterate" over each tuple element via std::apply and a fold expression
502 std::apply([&](auto&&... memberMeta) constexpr {
503 // Fold expression
504 ([&]{
505 // Meta
506 static constexpr auto mName = std::remove_reference<decltype(memberMeta)>::type::M_NAME;
507 constexpr QLatin1StringView mKey(mName.value);
508 using mType = typename std::remove_reference<decltype(memberMeta)>::type::M_TYPE;
509 auto& mRef = value.*(memberMeta.mPtr);
510
511 // Ignore if empty optional
512 if constexpr(json_optional<mType>)
513 {
514 if(!mRef)
515 return;
516 }
517
518 // Convert value and insert
519 if constexpr(json_override_convertible<T, mType, mName>)
520 jObject.insert(mKey, QxJsonPrivate::overrideSerialize<T, mType, mName>(mRef));
521 else
522 jObject.insert(mKey, QxJsonPrivate::standardSerialize<mType>(mRef));
523 }(), ...);
524 }, memberMetas);
525
526 return jObject;
527 }
528};
529
530template<typename T>
531 requires json_collective<T>
532struct Converter<T>
533{
534 using E = typename T::value_type;
535
536 static Qx::JsonError fromJson(T& value, const QJsonValue& jValue)
537 {
538 // Reset buffer
539 value.clear();
540
541 if(!jValue.isArray())
542 {
543 return Qx::JsonError(QxJsonPrivate::ERR_CONV_TYPE.arg(QxJsonPrivate::typeString<QJsonArray>()), Qx::JsonError::TypeMismatch)
545 }
546
547 // Underlying Array
548 QJsonArray jArray = jValue.toArray();
549
550 // Error tracking
551 Qx::JsonError cnvError;
552
553 // Convert all
554 for(auto i = 0; i < jArray.count(); ++i)
555 {
556 E converted;
557 if(cnvError = Converter<E>::fromJson(converted, jArray[i]); cnvError.isValid())
558 {
559 value.clear();
561 }
562
563 value << converted;
564 }
565
566 return Qx::JsonError();
567 }
568
569 static QJsonArray toJson(const T& value)
570 {
571 // Array to fill
572 QJsonArray jArray;
573
574 // Convert all
575 for(const E& e : value)
576 {
577 // Ignore if empty optional
578 if constexpr(json_optional<E>)
579 {
580 if(!e)
581 continue;
582 }
583
584 jArray.append(Converter<E>::toJson(e));
585 }
586
587 return jArray;
588 }
589};
590
591template<typename T>
592 requires json_associative<T>
593struct Converter<T>
594{
595 using K = typename T::key_type;
596 using V = typename T::mapped_type;
597
598 static Qx::JsonError fromJson(T& value, const QJsonValue& jValue)
599 {
600 // Reset buffer
601 value.clear();
602
603 if(!jValue.isArray())
604 {
605 return Qx::JsonError(QxJsonPrivate::ERR_CONV_TYPE.arg(QxJsonPrivate::typeString<QJsonArray>()), Qx::JsonError::TypeMismatch)
607 }
608 // Underlying Array
609 QJsonArray jArray = jValue.toArray();
610
611 // Error tracking
612 Qx::JsonError cnvError;
613
614 // Convert all
615 for(auto i = 0; i < jArray.count(); ++i)
616 {
617 V converted;
618 if(cnvError = Converter<V>::fromJson(converted, jArray[i]); cnvError.isValid())
619 {
620 value.clear();
622 }
623
624 value.insert(keygen<K, V>(converted), converted);
625 }
626
627 return Qx::JsonError();
628 }
629
630 static QJsonArray toJson(const T& value)
631 {
632 // Array to fill
633 QJsonArray jArray;
634
635 // Convert all
636 for(const V& v : value)
637 {
638 // Ignore if empty optional
639 if constexpr(json_optional<V>)
640 {
641 if(!v)
642 continue;
643 }
644
645 jArray.append(Converter<V>::toJson(v));
646 }
647
648 return jArray;
649 }
650};
651
652template<typename T>
653 requires json_optional<T>
654struct Converter<T>
655{
656 using O = typename T::value_type;
657
658 static Qx::JsonError fromJson(T& value, const QJsonValue& jValue)
659 {
660 O opt;
661 Qx::JsonError je = Converter<O>::fromJson(opt, jValue);
662
663 if(!je.isValid())
664 value = std::move(opt);
665
666 return je;
667 }
668
669 static auto toJson(const T& value)
670 {
671 Q_ASSERT(value); // Optional must have value if this is reached
672 return Converter<O>::toJson(*value);
673 }
674};
675
676template<typename T>
677 requires std::integral<T> && (!std::same_as<T, bool>)
678struct Converter<T>
679{
680 static Qx::JsonError fromJson(T& value, const QJsonValue& jValue)
681 {
682 if(!jValue.isDouble())
683 return Qx::JsonError(QxJsonPrivate::ERR_CONV_TYPE.arg(QxJsonPrivate::typeString<double>()), Qx::JsonError::TypeMismatch);
684
685 value = static_cast<T>(jValue.toDouble());
686 return Qx::JsonError();
687 }
688
689 static double toJson(const T& value)
690 {
691 return static_cast<double>(value);
692 }
693};
696} // namespace QxJson
697
698namespace Qx
699{
700
701//-Concepts (cont.)-------------------------------------------------------------------------------------------------
702template<typename T>
705
706//-Classes---------------------------------------------------------------------------------------------------------
707class QX_CORE_EXPORT QJsonParseErrorAdapter: public Qx::AbstractError<"QJsonParseError", 500>
708{
709 //-Class Variables-------------------------------------------------------------------------------------
710private:
711 static inline const QString OFFSET_STR = u"Position: %1."_s;
712
713 //-Instance Variables-------------------------------------------------------------------------------------
714private:
715 const QJsonParseError& mErrorRef;
716
717 //-Constructor---------------------------------------------------------------------------------------------
718public:
722
723 //-Instance Functions-------------------------------------------------------------------------------------
724private:
725 quint32 deriveValue() const override;
726 QString derivePrimary() const override;
727 QString deriveSecondary() const override;
728};
729
730//-Functions-------------------------------------------------------------------------------------------------------
731template<typename T>
733JsonError parseJson(T& parsed, const QJsonObject& obj)
734{
735 // Use QJsonValue for semi-type erasure
736 QJsonValue objAsValue(obj);
737
738 return QxJson::Converter<T>::fromJson(parsed, objAsValue);
739}
740
741template<typename T>
743void serializeJson(QJsonObject& serialized, const T& struc)
744{
745 serialized = QxJson::Converter<T>::toJson(struc);
746}
747
748template<typename T>
750JsonError parseJson(T& parsed, const QJsonArray& array)
751{
752 // Use QJsonValue for semi-type erasure
753 QJsonValue arrayAsValue(array);
754
755 return QxJson::Converter<T>::fromJson(parsed, arrayAsValue);
756}
757
758template<typename T>
760void serializeJson(QJsonArray& serialized, const T& container)
761{
762 serialized = QxJson::Converter<T>::toJson(container);
763}
764
765template<typename T>
766 requires json_root<T>
767JsonError parseJson(T& parsed, const QJsonDocument& doc)
768{
769 if(doc.isEmpty())
770 return JsonError(QxJsonPrivate::ERR_PARSE_DOC, JsonError::EmptyDoc).withContext(QxJson::Document());
771
772 if constexpr(QxJson::json_containing<T>)
773 {
774 if(!doc.isArray())
775 return JsonError(QxJsonPrivate::ERR_PARSE_DOC, JsonError::TypeMismatch).withContext(QxJson::Document());
776
777 return parseJson(parsed, doc.array()).withContext(QxJson::Document());
778 }
779 else
780 {
781 if(!doc.isObject())
782 return JsonError(QxJsonPrivate::ERR_PARSE_DOC, JsonError::TypeMismatch).withContext(QxJson::Document());
783
784 return parseJson(parsed, doc.object()).withContext(QxJson::Document());
785 }
786}
787
788template<typename T>
789 requires json_root<T>
790void serializeJson(QJsonDocument& serialized, const T& root)
791{
793}
794
795template<typename T>
796 requires json_root<T>
797JsonError parseJson(T& parsed, QFile& file)
798{
799 if(!file.exists())
800 return JsonError(QxJsonPrivate::ERR_READ_FILE, JsonError::MissingFile).withContext(QxJson::File(file.fileName()));
801
802 // Close and re-open file, if open, to ensure correct mode and start of file
803 if(file.isOpen())
804 file.close();
805
806 if(!file.open(QIODevice::ReadOnly))
807 return JsonError(QxJsonPrivate::ERR_READ_FILE, JsonError::InaccessibleFile).withContext(QxJson::File(file.fileName(), file.errorString()));
808
809 // Close file when finished
810 QScopeGuard fileGuard([&file]{ file.close(); });
811
812 // Read data
813 QByteArray jsonData = file.readAll();
814 if(jsonData.isEmpty())
815 {
816 if(file.error() != QFileDevice::NoError)
817 return JsonError(QxJsonPrivate::ERR_READ_FILE, JsonError::FileReadError).withContext(QxJson::File(file.fileName(), file.errorString()));
818 else
819 return JsonError(QxJsonPrivate::ERR_READ_FILE, JsonError::EmptyDoc).withContext(QxJson::File(file.fileName()));
820 }
821
822 // Basic parse
823 QJsonParseError jpe;
824 QJsonDocument jd = QJsonDocument::fromJson(jsonData, &jpe);
825
826 if(jpe.error != jpe.NoError)
827 return JsonError(QxJsonPrivate::ERR_READ_FILE, JsonError::FileReadError).withContext(QxJson::File(file.fileName(), jpe.errorString()));
828
829 // True parse
830 return parseJson(parsed, jd).withContext(QxJson::File(file.fileName()));
831}
832
833template<typename T>
834 requires json_root<T>
835JsonError serializeJson(QFile& serialized, const T& root)
836{
837 // Close and re-open file, if open, to ensure correct mode and start of file
838 if(serialized.isOpen())
839 serialized.close();
840
842 return JsonError(QxJsonPrivate::ERR_WRITE_FILE, JsonError::InaccessibleFile).withContext(QxJson::File(serialized.fileName(), serialized.errorString()));
843
844 // Close file when finished
845 QScopeGuard fileGuard([&serialized]{ serialized.close(); });
846
847 // Create document
848 QJsonDocument jd;
849 serializeJson(jd, root);
850
851 // Write data
852 QByteArray jsonData = jd.toJson();
853 if(jsonData.isEmpty())
854 return JsonError(); // No-op
855
856 if(serialized.write(jsonData) != jsonData.size())
857 return JsonError(QxJsonPrivate::ERR_WRITE_FILE, JsonError::FileWriteError).withContext(QxJson::File(serialized.fileName(), serialized.errorString()));
858
859 return JsonError();
860}
861
862template<typename T>
863 requires json_root<T>
864JsonError parseJson(T& parsed, const QString& filePath)
865{
866 QFile file(filePath);
867
868 return parseJson(parsed, file);
869}
870
871template<typename T>
872 requires json_root<T>
873JsonError serializeJson(const QString& serializedPath, const T& root)
874{
875 QFile file(serializedPath);
876
877 return serializeJson(file, root);
878}
879
880QX_CORE_EXPORT QList<QJsonValue> findAllValues(const QJsonValue& rootValue, QStringView key);
881QX_CORE_EXPORT QString asString(const QJsonValue& value);
882
883} // namespace Qx
885
886#endif // QX_JSON_H
The array element key class represents a JSON Array element node for use in error contexts.
Definition qx-json.h:138
The array class represents a JSON array node for use in error contexts.
Definition qx-json.h:130
The document class represents a JSON document node for use in error contexts.
Definition qx-json.h:100
The file class represents a JSON file node for use in error contexts.
Definition qx-json.h:86
The object key class represents a JSON Object key node for use in error contexts.
Definition qx-json.h:119
The object class represents a JSON object node for use in error contexts.
Definition qx-json.h:111
The AbstractError template class completes the Error interface and acts as the base class from which ...
Definition qx-abstracterror.h:86
The JsonError class is used to report errors related to JSON manipulation.
Definition qx-json.h:156
Form
Definition qx-json.h:160
@ EmptyDoc
Definition qx-json.h:164
@ InaccessibleFile
Definition qx-json.h:166
@ FileReadError
Definition qx-json.h:167
@ TypeMismatch
Definition qx-json.h:163
@ MissingKey
Definition qx-json.h:162
@ FileWriteError
Definition qx-json.h:168
@ MissingFile
Definition qx-json.h:165
JsonError & withContext(const QxJson::ContextNode &node)
Definition qx-json.cpp:227
bool isValid() const
Definition qx-json.cpp:196
Allows QJsonParseError to be used via the Qx::Error interface.
Definition qx-json.h:708
Specifies that a type is an associative container, the value type of which is convertible,...
Definition qx-json.h:319
Specifies that a type is a non-associative container, the value type of which is convertible to/from ...
Definition qx-json.h:315
Specifies that a type is a container, and abides by the other corresponding restrictions for that kin...
Definition qx-json.h:324
Specifies that a type is generally convertible to/from JSON.
Definition qx-json.h:295
Specifies that a type has a known method for creating a corresponding key.
Definition qx-json.h:310
Specifies that a type is a specialization of std::optional that manages values of a JSON convertible ...
Definition qx-json.h:328
Definition qx-json.h:301
Specifies that a type is a JSON-tied struct registered with QX_JSON_STRUCT().
Definition qx-json.h:281
Specifies that a type is a JSON-tied struct registered with QX_JSON_STRUCT_OUTSIDE().
Definition qx-json.h:286
Specifies that a type is a JSON-tied struct.
Definition qx-json.h:291
Specifies that a type is one of the fundamental JSON types within Qt's JSON system.
Definition qx-json.h:278
Specifies that a type is one of several types.
Definition qx-concepts.h:513
Specifies that a type is a valid analogue for a JSON document root element.
Definition qx-json.h:703
Specifies that a type is a Qt-based associative container type.
Definition qx-concepts.h:521
Specifies that a type is a Qt-based collection type.
Definition qx-concepts.h:525
Specifies that a type is a specialization of a template.
Definition qx-concepts.h:517
The QxJson namespace encapsulates the user-extensible implementation of Qx's JSON parsing facilities.
Definition qx-json.cpp:425
Key keygen(const Value &value)=delete
The keygen template function acts as an interface through which the derivation of a key for a given t...
std::variant< File, Document, Object, ObjectKey, Array, ArrayElement > ContextNode
Definition qx-json.h:148
The Qx namespace is the main namespace through which all non-global functionality of the Qx library i...
Definition qx-processwaiter.cpp:5
void serializeJson(QJsonObject &serialized, const T &struc)
Definition qx-json.h:743
QString asString(const QJsonValue &value)
Definition qx-json.cpp:399
JsonError parseJson(T &parsed, const QJsonObject &obj)
Definition qx-json.h:733
QList< QJsonValue > findAllValues(const QJsonValue &rootValue, QStringView key)
Definition qx-json.cpp:387
bool isEmpty() const const
qsizetype size() const const
bool exists(const QString &fileName)
virtual QString fileName() const const override
bool open(FILE *fh, QIODeviceBase::OpenMode mode, QFileDevice::FileHandleFlags handleFlags)
virtual void close() override
QFileDevice::FileError error() const const
QString errorString() const const
bool isOpen() const const
QByteArray readAll()
qint64 write(const QByteArray &data)
void append(const QJsonValue &value)
qsizetype count() const const
QJsonArray array() const const
QJsonDocument fromJson(const QByteArray &json, QJsonParseError *error)
bool isArray() const const
bool isEmpty() const const
bool isObject() const const
QJsonObject object() const const
bool contains(QLatin1StringView key) const const
QJsonObject::iterator insert(QLatin1StringView key, const QJsonValue &value)
QJsonValue value(QLatin1StringView key) const const
QString errorString() const const
bool isArray() const const
bool isBool() const const
bool isDouble() const const
bool isObject() const const
bool isString() const const
QJsonArray toArray() const const
bool toBool(bool defaultValue) const const
double toDouble(double defaultValue) const const
QJsonObject toObject() const const
QString toString() const const
The qx-abstracterror.h header file provides access to the base class from which custom error types sh...
The qx-concepts header file provides a library of general purpose concepts as an extension of the sta...
The qx-error.h header file provides access to the Error interface.
#define QX_DECLARE_ERROR_ADAPTATION(Adaptable, Adapter)
Definition qx-error.h:132
The qx-concepts header file provides a set of various object-like and function-like macros that are d...
The Converter template struct acts as an interface that carries details on how to parse/serialize JSO...
Definition qx-json.h:267
Definition qx-json.h:270
Definition qx-json.h:273
The StringLiteral template struct acts as a literal class type wrapper around a C-style string that e...
Definition qx-stringliteral.h:11