Qx v0.6
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:
171
172//-Class Variables-------------------------------------------------------------
173private:
174 static inline const QHash<Form, QString> ERR_STRINGS{
175 {NoError, u""_s},
176 {MissingKey, u"The key does not exist."_s},
177 {TypeMismatch, u"Value type mismatch."_s},
178 {EmptyDoc, u"The document is empty."_s},
179 {InvalidValue, u"Invalid value for type."_s},
180 {MissingFile, u"File does not exist."_s},
181 {InaccessibleFile, u"Cannot open the file."_s},
182 {FileReadError, u"File read error."_s},
183 {FileWriteError, u"File write error."_s}
184 };
185
186//-Instance Variables-------------------------------------------------------------
187private:
188 QString mAction;
189 Form mForm;
191
192//-Constructor-----------------------------------------------------------------
193public:
194 JsonError();
195 JsonError(const QString& a, Form f);
196
197//-Instance Functions-------------------------------------------------------------
198private:
199 quint32 deriveValue() const override;
200 QString derivePrimary() const override;
201 QString deriveSecondary() const override;
202 QString deriveDetails() const override;
203
204public:
205 bool isValid() const;
206 QString action() const;
207 Form form() const;
208 QList<QxJson::ContextNode> context() const;
209 JsonError& withContext(const QxJson::ContextNode& node);
210};
211
212} // namespace Qx
213
214namespace QxJson
215{
216//-Structs---------------------------------------------------------------
217template<typename T>
219
220template<class Struct, Qx::StringLiteral member>
221struct MemberOverrideCoverter;
222
223template<typename SelfType, typename DelayedSelfType>
224struct QxJsonMetaStructOutside;
225
226//-Concepts--------------------------------------------------------------
227
228template<typename T>
230
231template<typename T>
232concept json_struct_inside = requires {
233 T::template QxJsonMetaStructInside<T>::memberMetadata();
234};
235
236template<typename T>
237concept json_struct_outside = requires {
238 QxJsonMetaStructOutside<T, T>::memberMetadata();
239};
240
241template<typename T>
244
245template<typename T>
246concept json_convertible = requires(T& tValue) {
247 { Converter<T>::fromJson(tValue, QJsonValue()) } -> std::same_as<Qx::JsonError>;
248 { Converter<T>::toJson(tValue) } -> qjson_type;
249};
250
251template<class K, typename T, Qx::StringLiteral N>
252concept json_override_convertible = requires(T& tValue) {
253 { MemberOverrideCoverter<K, N>::fromJson(tValue, QJsonValue()) } -> std::same_as<Qx::JsonError>;
254 { MemberOverrideCoverter<K, N>::toJson(tValue) } -> qjson_type;
255};
256
257template<typename Key, class Value>
258Key keygen(const Value& value) = delete;
259
260template<typename Key, class Value>
261concept json_keyable = requires(const Value& v) {
262 { keygen<Key, Value>(v) } -> std::same_as<Key>;
263};
264
265template<typename T>
268
269template<typename T>
273
274template<typename T>
277
278template<typename T>
281
282} // namespace QxJson
283
285namespace QxJsonPrivate
286{
287//-Namespace Variables---------------------------------------------------
288static inline const QString ERR_CONV_TYPE = u"JSON Error: Converting value to %1"_s;
289static inline const QString ERR_NO_KEY = u"JSON Error: Could not retrieve key '%1'."_s;
290static inline const QString ERR_PARSE_DOC = u"JSON Error: Could not parse JSON document."_s;
291static inline const QString ERR_READ_FILE = u"JSON Error: Could not read JSON file."_s;
292static inline const QString ERR_WRITE_FILE = u"JSON Error: Could not write JSON file."_s;
293
294//-Structs---------------------------------------------------------------
295template<Qx::StringLiteral MemberN, typename MemberT, class Struct>
296struct MemberMetadata
297{
298 constexpr static Qx::StringLiteral M_NAME = MemberN;
299 typedef MemberT M_TYPE;
300 MemberT Struct::* mPtr;
301};
302
303//-Functions-------------------------------------------------------------
304template <Qx::StringLiteral N, typename T, class S>
305constexpr MemberMetadata<N, T, S> makeMemberMetadata(T S::*memberPtr)
306{
307 return {memberPtr};
308}
309
310template<typename T> [[maybe_unused]] static inline QString typeString() = delete;
311template<typename T> [[maybe_unused]] static inline bool isType(const QJsonValue& v) = delete;
312template<typename T> [[maybe_unused]] static inline T toType(const QJsonValue& v) = delete;
313
314template<> inline QString typeString<bool>() { return u"bool"_s; };
315template<> inline QString typeString<double>() { return u"double"_s; };
316template<> inline QString typeString<QString>() { return u"string"_s; };
317template<> inline QString typeString<QJsonArray>() { return u"array"_s; };
318template<> inline QString typeString<QJsonObject>() { return u"object"_s; };
319
320template<> inline bool isType<bool>(const QJsonValue& v) { return v.isBool(); };
321template<> inline bool isType<double>(const QJsonValue& v) { return v.isDouble(); };
322template<> inline bool isType<QString>(const QJsonValue& v) { return v.isString(); };
323template<> inline bool isType<QJsonArray>(const QJsonValue& v) { return v.isArray(); };
324template<> inline bool isType<QJsonObject>(const QJsonValue& v) { return v.isObject(); };
325
326template<> inline bool toType<bool>(const QJsonValue& v) { return v.toBool(); };
327template<> inline double toType<double>(const QJsonValue& v) { return v.toDouble(); };
328template<> inline QString toType<QString>(const QJsonValue& v) { return v.toString(); };
329template<> inline QJsonArray toType<QJsonArray>(const QJsonValue& v) { return v.toArray(); };
330template<> inline QJsonObject toType<QJsonObject>(const QJsonValue& v) { return v.toObject(); };
331
332//-Functions-------------------------------------------------------------
333/* These helpers are required as a form on indirection within
334 *
335 * template<typename T>
336 * requires json_struct<T>
337 * struct Converter<T> {...}
338 *
339 * That functions makes/made use of 'if constexpr' statements that contain
340 * references to a type in their true branches that doesn't exist if the
341 * false branches are taken. Different compilers seem to discard the untaken
342 * branch of the value dependent statement at different stages as it compiled
343 * fine with MSVC and a newer version of GCC, but not clang or older GCC versions.
344 * To clarify, compilation would fail due to the type in the not-yet-discarded
345 * branch not being declared.
346 *
347 * Putting the reference of the potentially undeclared types behind these functions
348 * that always exist solves the issue.
349 *
350 * These likely can be avoided with C++23's 'if consteval'.
351 */
352template<class K>
354constexpr auto getMemberMeta()
355{
356 return K::template QxJsonMetaStructInside<K>::memberMetadata();
357}
358
359template<class K>
361constexpr auto getMemberMeta()
362{
363 return QxJson::QxJsonMetaStructOutside<K, K>::memberMetadata();
364}
365
366template<class K, typename T, Qx::StringLiteral N>
367 requires QxJson::json_override_convertible<K, T, N>
368Qx::JsonError overrideParse(T& value, const QJsonValue& jv)
369{
370 return QxJson::MemberOverrideCoverter<K, N>::fromJson(value, jv);
371}
372
373template<class K, typename T, Qx::StringLiteral N>
374 requires QxJson::json_override_convertible<K, T, N>
375auto overrideSerialize(const T& value)
376{
377 return QxJson::MemberOverrideCoverter<K, N>::toJson(value);
378}
379
380template<typename T>
382Qx::JsonError standardParse(T& value, const QJsonValue& jv)
383{
384 return QxJson::Converter<T>::fromJson(value, jv);
385}
386
387template<typename T>
389auto standardSerialize(const T& value)
390{
391 return QxJson::Converter<T>::toJson(value);
392}
395} // namespace QxJsonPrivate
396
397
398namespace QxJson
399{
400//-Default Converter Specializations-------------------------------------
402template<typename T>
403 requires qjson_type<T>
404struct Converter<T>
405{
406 static Qx::JsonError fromJson(T& value, const QJsonValue& jValue)
407 {
408 if(!QxJsonPrivate::isType<T>(jValue))
409 return Qx::JsonError(QxJsonPrivate::ERR_CONV_TYPE.arg(QxJsonPrivate::typeString<T>()), Qx::JsonError::TypeMismatch);
410
411 value = QxJsonPrivate::toType<T>(jValue);
412 return Qx::JsonError();
413 }
414
415 static T toJson(const T& value)
416 {
417 /* We could automatically box with QJsonValue here, but it's technically unnecessary as every possible
418 * type returned here is implicitly convertible to QJsonValue. Additionally, doing so would complicate
419 * the json_convertible concept as it would have to allow QJsonValue as well as the underlying
420 * types.
421 */
422 return value; // No-op
423 }
424};
425
426template<typename T>
427 requires json_struct<T>
428struct Converter<T>
429{
430 static Qx::JsonError fromJson(T& value, const QJsonValue& jValue)
431 {
432 if(!jValue.isObject())
433 return Qx::JsonError(QxJsonPrivate::ERR_CONV_TYPE.arg(QxJsonPrivate::typeString<QJsonObject>()), Qx::JsonError::TypeMismatch)
435
436 // Underlying object
437 QJsonObject jObject = jValue.toObject();
438
439 // Error tracker
440 Qx::JsonError cnvError;
441
442 // Get member metadata tuple
443 constexpr auto memberMetas = QxJsonPrivate::getMemberMeta<T>();
444
445 // "Iterate" over each tuple element via std::apply, with a fold expression
446 // utilizing && which short-circuits. This allows us to "break" the loop
447 // upon a member conversion failure as single return of false in the inner-most
448 // lambda will trigger the short-circuit.
449 std::apply([&](auto&&... memberMeta) constexpr {
450 // Fold expression
451 ([&]{
452 // Meta
453 static constexpr auto mName = std::remove_reference<decltype(memberMeta)>::type::M_NAME;
454 constexpr QLatin1StringView mKey(mName.value);
455 using mType = typename std::remove_reference<decltype(memberMeta)>::type::M_TYPE;
456 auto& mRef = value.*(memberMeta.mPtr);
457
458 // Get value from key
459 if(!jObject.contains(mKey))
460 {
461 if constexpr(json_optional<mType>)
462 {
463 mRef = std::nullopt;
464 return true;
465 }
466 else
467 {
468 cnvError = Qx::JsonError(QxJsonPrivate::ERR_NO_KEY.arg(mKey), Qx::JsonError::MissingKey)
470 return false;
471 }
472 }
473 QJsonValue mValue = jObject.value(mKey);
474
475 // Convert value
476 if constexpr(json_override_convertible<T, mType, mName>)
477 cnvError = QxJsonPrivate::overrideParse<T, mType, mName>(mRef, mValue);
478 else
479 cnvError = QxJsonPrivate::standardParse<mType>(mRef, mValue);
480
482 return !cnvError.isValid();
483 }() && ...);
484 }, memberMetas);
485
486 return cnvError;
487 }
488
489 static QJsonObject toJson(const T& value)
490 {
491 // Object to fill
492 QJsonObject jObject;
493
494 // Get member metadata tuple
495 constexpr auto memberMetas = QxJsonPrivate::getMemberMeta<T>();
496
497 // "Iterate" over each tuple element via std::apply and a fold expression
498 std::apply([&](auto&&... memberMeta) constexpr {
499 // Fold expression
500 ([&]{
501 // Meta
502 static constexpr auto mName = std::remove_reference<decltype(memberMeta)>::type::M_NAME;
503 constexpr QLatin1StringView mKey(mName.value);
504 using mType = typename std::remove_reference<decltype(memberMeta)>::type::M_TYPE;
505 auto& mRef = value.*(memberMeta.mPtr);
506
507 // Ignore if empty optional
508 if constexpr(json_optional<mType>)
509 {
510 if(!mRef)
511 return;
512 }
513
514 // Convert value and insert
515 if constexpr(json_override_convertible<T, mType, mName>)
516 jObject.insert(mKey, QxJsonPrivate::overrideSerialize<T, mType, mName>(mRef));
517 else
518 jObject.insert(mKey, QxJsonPrivate::standardSerialize<mType>(mRef));
519 }(), ...);
520 }, memberMetas);
521
522 return jObject;
523 }
524};
525
526template<typename T>
527 requires json_collective<T>
528struct Converter<T>
529{
530 using E = typename T::value_type;
531
532 static Qx::JsonError fromJson(T& value, const QJsonValue& jValue)
533 {
534 // Reset buffer
535 value.clear();
536
537 if(!jValue.isArray())
538 {
539 return Qx::JsonError(QxJsonPrivate::ERR_CONV_TYPE.arg(QxJsonPrivate::typeString<QJsonArray>()), Qx::JsonError::TypeMismatch)
541 }
542
543 // Underlying Array
544 QJsonArray jArray = jValue.toArray();
545
546 // Error tracking
547 Qx::JsonError cnvError;
548
549 // Convert all
550 for(auto i = 0; i < jArray.count(); ++i)
551 {
552 E converted;
553 if(cnvError = Converter<E>::fromJson(converted, jArray[i]); cnvError.isValid())
554 {
555 value.clear();
557 }
558
559 value << converted;
560 }
561
562 return Qx::JsonError();
563 }
564
565 static QJsonArray toJson(const T& value)
566 {
567 // Array to fill
568 QJsonArray jArray;
569
570 // Convert all
571 for(const E& e : value)
572 {
573 // Ignore if empty optional
574 if constexpr(json_optional<E>)
575 {
576 if(!e)
577 continue;
578 }
579
580 jArray.append(Converter<E>::toJson(e));
581 }
582
583 return jArray;
584 }
585};
586
587template<typename T>
588 requires json_associative<T>
589struct Converter<T>
590{
591 using K = typename T::key_type;
592 using V = typename T::mapped_type;
593
594 static Qx::JsonError fromJson(T& value, const QJsonValue& jValue)
595 {
596 // Reset buffer
597 value.clear();
598
599 if(!jValue.isArray())
600 {
601 return Qx::JsonError(QxJsonPrivate::ERR_CONV_TYPE.arg(QxJsonPrivate::typeString<QJsonArray>()), Qx::JsonError::TypeMismatch)
603 }
604 // Underlying Array
605 QJsonArray jArray = jValue.toArray();
606
607 // Error tracking
608 Qx::JsonError cnvError;
609
610 // Convert all
611 for(auto i = 0; i < jArray.count(); ++i)
612 {
613 V converted;
614 if(cnvError = Converter<V>::fromJson(converted, jArray[i]); cnvError.isValid())
615 {
616 value.clear();
618 }
619
620 value.insert(keygen<K, V>(converted), converted);
621 }
622
623 return Qx::JsonError();
624 }
625
626 static QJsonArray toJson(const T& value)
627 {
628 // Array to fill
629 QJsonArray jArray;
630
631 // Convert all
632 for(const V& v : value)
633 {
634 // Ignore if empty optional
635 if constexpr(json_optional<V>)
636 {
637 if(!v)
638 continue;
639 }
640
641 jArray.append(Converter<V>::toJson(v));
642 }
643
644 return jArray;
645 }
646};
647
648template<typename T>
649 requires json_optional<T>
650struct Converter<T>
651{
652 using O = typename T::value_type;
653
654 static Qx::JsonError fromJson(T& value, const QJsonValue& jValue)
655 {
656 O opt;
657 Qx::JsonError je = Converter<O>::fromJson(opt, jValue);
658
659 if(!je.isValid())
660 value = std::move(opt);
661
662 return je;
663 }
664
665 static auto toJson(const T& value)
666 {
667 Q_ASSERT(value); // Optional must have value if this is reached
668 return Converter<O>::toJson(*value);
669 }
670};
671
672template<typename T>
673 requires std::integral<T> && (!std::same_as<T, bool>)
674struct Converter<T>
675{
676 static Qx::JsonError fromJson(T& value, const QJsonValue& jValue)
677 {
678 if(!jValue.isDouble())
679 return Qx::JsonError(QxJsonPrivate::ERR_CONV_TYPE.arg(QxJsonPrivate::typeString<double>()), Qx::JsonError::TypeMismatch);
680
681 value = static_cast<T>(jValue.toDouble());
682 return Qx::JsonError();
683 }
684
685 static double toJson(const T& value)
686 {
687 return static_cast<double>(value);
688 }
689};
692} // namespace QxJson
693
694namespace Qx
695{
696
697//-Concepts (cont.)-------------------------------------------------------------------------------------------------
698template<typename T>
701
702//-Classes---------------------------------------------------------------------------------------------------------
703class QX_CORE_EXPORT QJsonParseErrorAdapter: public Qx::AbstractError<"QJsonParseError", 500>
704{
705 //-Class Variables-------------------------------------------------------------------------------------
706private:
707 static inline const QString OFFSET_STR = u"Position: %1."_s;
708
709 //-Instance Variables-------------------------------------------------------------------------------------
710private:
711 const QJsonParseError& mErrorRef;
712
713 //-Constructor---------------------------------------------------------------------------------------------
714public:
718
719 //-Instance Functions-------------------------------------------------------------------------------------
720private:
721 quint32 deriveValue() const override;
722 QString derivePrimary() const override;
723 QString deriveSecondary() const override;
724};
725
726//-Functions-------------------------------------------------------------------------------------------------------
727template<typename T>
729JsonError parseJson(T& parsed, const QJsonObject& obj)
730{
731 // Use QJsonValue for semi-type erasure
732 QJsonValue objAsValue(obj);
733
734 return QxJson::Converter<T>::fromJson(parsed, objAsValue);
735}
736
737template<typename T>
739void serializeJson(QJsonObject& serialized, const T& struc)
740{
741 serialized = QxJson::Converter<T>::toJson(struc);
742}
743
744template<typename T>
746JsonError parseJson(T& parsed, const QJsonArray& array)
747{
748 // Use QJsonValue for semi-type erasure
749 QJsonValue arrayAsValue(array);
750
751 return QxJson::Converter<T>::fromJson(parsed, arrayAsValue);
752}
753
754template<typename T>
756void serializeJson(QJsonArray& serialized, const T& container)
757{
758 serialized = QxJson::Converter<T>::toJson(container);
759}
760
761template<typename T>
762 requires json_root<T>
763JsonError parseJson(T& parsed, const QJsonDocument& doc)
764{
765 if(doc.isEmpty())
766 return JsonError(QxJsonPrivate::ERR_PARSE_DOC, JsonError::EmptyDoc).withContext(QxJson::Document());
767
768 if constexpr(QxJson::json_containing<T>)
769 {
770 if(!doc.isArray())
771 return JsonError(QxJsonPrivate::ERR_PARSE_DOC, JsonError::TypeMismatch).withContext(QxJson::Document());
772
773 return parseJson(parsed, doc.array()).withContext(QxJson::Document());
774 }
775 else
776 {
777 if(!doc.isObject())
778 return JsonError(QxJsonPrivate::ERR_PARSE_DOC, JsonError::TypeMismatch).withContext(QxJson::Document());
779
780 return parseJson(parsed, doc.object()).withContext(QxJson::Document());
781 }
782}
783
784template<typename T>
785 requires json_root<T>
786void serializeJson(QJsonDocument& serialized, const T& root)
787{
789}
790
791template<typename T>
792 requires json_root<T>
793JsonError parseJson(T& parsed, QFile& file)
794{
795 if(!file.exists())
796 return JsonError(QxJsonPrivate::ERR_READ_FILE, JsonError::MissingFile).withContext(QxJson::File(file.fileName()));
797
798 // Close and re-open file, if open, to ensure correct mode and start of file
799 if(file.isOpen())
800 file.close();
801
802 if(!file.open(QIODevice::ReadOnly))
803 return JsonError(QxJsonPrivate::ERR_READ_FILE, JsonError::InaccessibleFile).withContext(QxJson::File(file.fileName(), file.errorString()));
804
805 // Close file when finished
806 QScopeGuard fileGuard([&file]{ file.close(); });
807
808 // Read data
809 QByteArray jsonData = file.readAll();
810 if(jsonData.isEmpty())
811 {
812 if(file.error() != QFileDevice::NoError)
813 return JsonError(QxJsonPrivate::ERR_READ_FILE, JsonError::FileReadError).withContext(QxJson::File(file.fileName(), file.errorString()));
814 else
815 return JsonError(QxJsonPrivate::ERR_READ_FILE, JsonError::EmptyDoc).withContext(QxJson::File(file.fileName()));
816 }
817
818 // Basic parse
819 QJsonParseError jpe;
820 QJsonDocument jd = QJsonDocument::fromJson(jsonData, &jpe);
821
822 if(jpe.error != jpe.NoError)
823 return JsonError(QxJsonPrivate::ERR_READ_FILE, JsonError::FileReadError).withContext(QxJson::File(file.fileName(), jpe.errorString()));
824
825 // True parse
826 return parseJson(parsed, jd).withContext(QxJson::File(file.fileName()));
827}
828
829template<typename T>
830 requires json_root<T>
831JsonError serializeJson(QFile& serialized, const T& root)
832{
833 // Close and re-open file, if open, to ensure correct mode and start of file
834 if(serialized.isOpen())
835 serialized.close();
836
838 return JsonError(QxJsonPrivate::ERR_WRITE_FILE, JsonError::InaccessibleFile).withContext(QxJson::File(serialized.fileName(), serialized.errorString()));
839
840 // Close file when finished
841 QScopeGuard fileGuard([&serialized]{ serialized.close(); });
842
843 // Create document
844 QJsonDocument jd;
845 serializeJson(jd, root);
846
847 // Write data
848 QByteArray jsonData = jd.toJson();
849 if(jsonData.isEmpty())
850 return JsonError(); // No-op
851
852 if(serialized.write(jsonData) != jsonData.size())
853 return JsonError(QxJsonPrivate::ERR_WRITE_FILE, JsonError::FileWriteError).withContext(QxJson::File(serialized.fileName(), serialized.errorString()));
854
855 return JsonError();
856}
857
858template<typename T>
859 requires json_root<T>
860JsonError parseJson(T& parsed, const QString& filePath)
861{
862 QFile file(filePath);
863
864 return parseJson(parsed, file);
865}
866
867template<typename T>
868 requires json_root<T>
869JsonError serializeJson(const QString& serializedPath, const T& root)
870{
871 QFile file(serializedPath);
872
873 return serializeJson(file, root);
874}
875
876QX_CORE_EXPORT QList<QJsonValue> findAllValues(const QJsonValue& rootValue, QStringView key);
877QX_CORE_EXPORT QString asString(const QJsonValue& value);
878
879} // namespace Qx
881
882#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:167
@ InvalidValue
Definition qx-json.h:165
@ FileReadError
Definition qx-json.h:168
@ TypeMismatch
Definition qx-json.h:163
@ MissingKey
Definition qx-json.h:162
@ FileWriteError
Definition qx-json.h:169
@ MissingFile
Definition qx-json.h:166
JsonError & withContext(const QxJson::ContextNode &node)
Definition qx-json.cpp:220
bool isValid() const
Definition qx-json.cpp:189
Allows QJsonParseError to be used via the Qx::Error interface.
Definition qx-json.h:704
Specifies that a type is an associative container, the value type of which is convertible,...
Definition qx-json.h:270
Specifies that a type is a non-associative container, the value type of which is convertible to/from ...
Definition qx-json.h:266
Specifies that a type is a container, and abides by the other corresponding restrictions for that kin...
Definition qx-json.h:275
Specifies that a type is generally convertible to/from JSON.
Definition qx-json.h:246
Specifies that a type has a known method for creating a corresponding key.
Definition qx-json.h:261
Specifies that a type is a specialization of std::optional that manages values of a JSON convertible ...
Definition qx-json.h:279
Specifies that a type is a JSON-tied struct registered with QX_JSON_STRUCT().
Definition qx-json.h:232
Specifies that a type is a JSON-tied struct registered with QX_JSON_STRUCT_OUTSIDE().
Definition qx-json.h:237
Specifies that a type is a JSON-tied struct.
Definition qx-json.h:242
Specifies that a type is one of the fundamental JSON types within Qt's JSON system.
Definition qx-json.h:229
Specifies that a type is one of several types.
Definition qx-concepts.h:502
Specifies that a type is a valid analogue for a JSON document root element.
Definition qx-json.h:699
Specifies that a type is a Qt-based associative container type.
Definition qx-concepts.h:510
Specifies that a type is a Qt-based collection type.
Definition qx-concepts.h:514
Specifies that a type is a specialization of a template.
Definition qx-concepts.h:506
The QxJson namespace encapsulates the user-extensible implementation of Qx's JSON parsing facilities.
Definition qx-json.cpp:418
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-abstracterror.cpp:13
void serializeJson(QJsonObject &serialized, const T &struc)
Definition qx-json.h:739
QString asString(const QJsonValue &value)
Definition qx-json.cpp:392
JsonError parseJson(T &parsed, const QJsonObject &obj)
Definition qx-json.h:729
QList< QJsonValue > findAllValues(const QJsonValue &rootValue, QStringView key)
Definition qx-json.cpp:380
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:218
The StringLiteral template struct acts as a literal class type wrapper around a C-style string that e...
Definition qx-stringliteral.h:11