Skip to content

Commit d840e9a

Browse files
committed
Array of tagged objects now can be stored on a TaggedJSONArray objects
1 parent b29df21 commit d840e9a

File tree

6 files changed

+426
-202
lines changed

6 files changed

+426
-202
lines changed

CMakeLists.txt

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,12 @@ set(CMAKE_AUTORCC ON)
99
set(CMAKE_CXX_STANDARD 11)
1010
set(CMAKE_CXX_STANDARD_REQUIRED ON)
1111

12+
if(MSVC)
13+
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /MT")
14+
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /MTd")
15+
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /Zc:preprocessor")
16+
endif()
17+
1218

1319
######################### Examples ############################
1420
find_package(QT NAMES Qt6 Qt5 REQUIRED COMPONENTS Core)
@@ -17,6 +23,7 @@ find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Core)
1723
add_executable(Example
1824
Examples/example.cpp
1925
inc/taggedjsonobject.h
26+
inc/taggedjsonarray.h
2027
Examples/example.json
2128
inc/taggedjsonobjectmacros.h
2229
inc/map.h
@@ -39,7 +46,10 @@ if(NOT googletest_POPULATED)
3946
endif()
4047

4148
# Test files
42-
add_executable(testRunner Tests/taggedjsonobject_test.cpp
49+
add_executable(testRunner
50+
Tests/taggedjsonobject_test.cpp
51+
Tests/test_main.cpp
52+
Tests/taggedjsonarray_test.cpp
4353
)
4454
target_include_directories(testRunner PRIVATE inc)
4555
target_link_libraries(testRunner

Examples/example.cpp

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
#include "taggedjsonobject.h"
2+
#include "taggedjsonarray.h"
23
#include "taggedjsonobjectmacros.h"
34

45
/*
@@ -16,10 +17,10 @@ Let's say, example.json file contains the following:
1617
}
1718
*/
1819

19-
DEFINE_JSON_TAGGED_OBJECT(InnerClass,
20+
TJO_DEFINE_JSON_TAGGED_OBJECT(InnerClass,
2021
(TaggedJSONString, example_sub_str))
2122

22-
DEFINE_JSON_TAGGED_OBJECT(OuterClass,
23+
TJO_DEFINE_JSON_TAGGED_OBJECT(OuterClass,
2324
(TaggedJSONInt, example_int),
2425
(TaggedJSONString, example_str),
2526
(TaggedJSONDouble, example_double),
@@ -28,6 +29,7 @@ DEFINE_JSON_TAGGED_OBJECT(OuterClass,
2829
(TaggedJSONVariantArray, example_mixed_arr))
2930

3031

32+
3133
int main(int argc, char *argv[])
3234
{
3335
QString jsonLocation("../../Examples/example.json");

Examples/example.json

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,15 @@
55
"example_sub_class":{
66
"example_sub_str": "Hello from an object!"
77
},
8+
"example_json_value1": {"test_value": 12},
9+
"example_json_value2": [1, 2, 3],
10+
"example_json_object": {"test_value": 12},
11+
812
"example_arr": ["Hello", "World"],
9-
"example_mixed_arr": [42, "is", "the", "answer", "to", "everything"]
13+
"example_mixed_arr": [42, "is", "the", "answer", "to", "everything"],
14+
"example_tagged_object_array": [
15+
{"name": "John", "age": 24},
16+
{"name": "Michael", "age": 46},
17+
{"name": "Sarah", "age": 5}
18+
]
1019
}

inc/taggedjsonarray.h

Lines changed: 280 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,280 @@
1+
#ifndef TAGGEDJSONARRAY_H
2+
#define TAGGEDJSONARRAY_H
3+
#include <type_traits>
4+
#include <QJsonObject>
5+
#include <QJsonArray>
6+
#include <QVector>
7+
8+
//! All types that can be encapsulated in QJsonObject
9+
#define TJO_JSON_COMPATIBLE std::is_arithmetic_v<T> || std::is_same_v<T, QJsonValue> || std::is_same_v<T, QJsonObject>|| std::is_same_v<T, QString> || std::is_same_v<T, QVariant>
10+
11+
//! Only objects that have been defined with the #TJO_DEFINE_JSON_TAGGED_OBJECT() macro
12+
#define TJO_IS_TAGGED_OBJECT std::is_constructible_v<T, QJsonValue, const bool> &&\
13+
std::is_constructible_v<T, QJsonObject, const bool> &&\
14+
std::is_constructible_v<T, const QString&, const bool> &&\
15+
std::is_constructible_v<T, const QByteArray&, const bool>
16+
17+
namespace {
18+
template<typename T>
19+
std::vector<T> extractFromQJSONArray(QJsonArray arr, const bool checkValue)
20+
{
21+
std::vector<T> out;
22+
out.reserve(arr.size());
23+
foreach(const QJsonValue & val, arr) {
24+
out.emplace_back(val, checkValue);
25+
}
26+
27+
return out;
28+
}
29+
}
30+
31+
32+
//! Invalid TaggedJSONArray template for SFINAE
33+
template<typename T, typename = void>
34+
class TaggedJSONArray;
35+
36+
/*!
37+
* \brief The TaggedJSONArray class variation that accepts JSON compatible data as its container.
38+
*
39+
* This class is used for storing JSON arrays with a preknown type (QVariantArrays can be used for mixed types)\n
40+
* Main usage for this class is to treat JSON data as class member for the tagged objects that has been generated from the #TJO_DEFINE_JSON_TAGGED_OBJECT() macro.\n
41+
* TaggedJSONArray can store all types of data JSON format accepts but TaggedJSONArray<T, typename std::enable_if_t< std::is_constructible_v<T, QJsonValue, bool>&& std::is_constructible_v<T, QString, bool>&& std::is_constructible_v<T, QByteArray, bool>&& std::is_constructible_v <T, QJsonObject, bool>>>
42+
* is more suitable for chaining JSON objects within JSON arrays.
43+
*/
44+
template<typename T>
45+
class TaggedJSONArray <T, typename std::enable_if_t<TJO_JSON_COMPATIBLE>>
46+
{
47+
public:
48+
/*!
49+
* \brief TaggedJSONArray constructor variant that takes QJsonArray input directly
50+
* \param val target JSON array data
51+
*/
52+
explicit TaggedJSONArray(QJsonArray val) : m_arr(std::move(val)) {};
53+
54+
/*!
55+
* \brief TaggedJSONArray constructor variant that takes QJsonValue input
56+
*
57+
* This constructor is intended for the #TJO_DEFINE_JSON_TAGGED_OBJECT() macro
58+
* \param ref target JSON array data
59+
* \param checkValue If set to true, invalid conversions (missing value, wrong type etc.) will throw a runtime error.
60+
*/
61+
explicit TaggedJSONArray(const QJsonValue& ref, const bool checkValue = true) : m_arr(ref.toArray()) {
62+
//Check if there is a valid data if it's intended
63+
if (checkValue && ref.isUndefined())
64+
throw(std::runtime_error("Invalid data has been encountered while parsing the json data for TaggedJSONArray"));
65+
}
66+
67+
//! Assignment operator setter
68+
template<typename V, typename = std::enable_if_t<std::is_convertible_v<V, QJsonArray>>>
69+
void operator=(V&& val) { m_arr = std::forward<V>(val); };
70+
71+
//Operator overloads
72+
bool operator!=(const QJsonArray& other) const { return m_arr != other; };
73+
bool operator==(const QJsonArray& other) const { return m_arr == other; };
74+
75+
/*!
76+
* \brief operator * Operator overload for the whole JSON array
77+
*
78+
* If the contents of the array desired to be taken as std::vector or QVector, see toStdVector() and toQvector()
79+
* \return Stored JSON array
80+
*/
81+
QJsonArray& operator*() { return m_arr; };
82+
83+
/*!
84+
* \brief operator * Operator overload for the whole JSON array
85+
*
86+
* If the contents of the array desired to be taken as std::vector or QVector, see toStdVector() and toQvector()
87+
* \return Stored JSON array
88+
*/
89+
const QJsonArray& operator*() const { return m_arr; };
90+
91+
/*!
92+
* \brief operator -> This operator is a shortcut for QJSONArray methods
93+
* \return Stored JSON array
94+
*/
95+
const QJsonArray* operator->() const { return &m_arr; };
96+
97+
/*!
98+
* \brief operator [] Shortcut for the mutable access operator for the storage.
99+
* \param i index to be accessed
100+
* \return i'th element of the JSON array
101+
*/
102+
QJsonValueRef operator[](const qsizetype i) { return m_arr[i]; };
103+
104+
/*!
105+
* \brief operator [] Shortcut for the immutable access operator for the storage.
106+
* \param i index to be accessed
107+
* \return i'th element of the JSON array
108+
*/
109+
T operator[](const qsizetype i) const { return dispatchValue(m_arr[i]); };
110+
111+
/*!
112+
* \brief at Shortcut for the mutable access for the storage.
113+
* \param i index to be accessed
114+
* \return i'th element of the JSON array
115+
*/
116+
QJsonValueRef at(const qsizetype i) { return m_arr[i]; };
117+
118+
/*!
119+
* \brief at Shortcut for the immutable access for the storage.
120+
* \param i index to be accessed
121+
* \return i'th element of the JSON array
122+
*/
123+
T at(const qsizetype i) const { return dispatchValue(m_arr[i]); };
124+
125+
//!\brief operator QString QString constructor variant for qDebug stream access.
126+
operator QString() const {
127+
QString ret;
128+
129+
for (const auto& curVal : m_arr)
130+
ret.append(QString(dispatchValue(curVal)) + "\n");
131+
return ret;
132+
};
133+
134+
/*!
135+
* \brief toStdVector Converts stored JSON array to a std::vector with the class templated type
136+
* \return std::vector of the stored data with the class templated type
137+
*/
138+
std::vector<T> toStdVector() const {
139+
std::vector<T> ret;
140+
ret.reserve(m_arr.size());
141+
142+
for (const auto& curVal : m_arr)
143+
ret.push_back(dispatchValue(curVal));
144+
return ret;
145+
};
146+
147+
/*!
148+
* \brief toQVector Converts stored JSON array to a QVector with the class templated type
149+
* \return QVector of the stored data with the class templated type
150+
*/
151+
QVector<T> toQVector() const {
152+
QVector<T> ret;
153+
ret.reserve(m_arr.size());
154+
155+
for (const auto& curVal : m_arr)
156+
ret.append(dispatchValue(curVal));
157+
return ret;
158+
}
159+
160+
private:
161+
QJsonArray m_arr;
162+
163+
template<class TO>
164+
friend std::ostream& operator<< (std::ostream& stream, const TaggedJSONArray<TO>& obj);
165+
166+
T dispatchValue(const QJsonValue& ref) const {
167+
if constexpr (std::is_same_v<T, bool>)
168+
return ref.toBool();
169+
else if constexpr (std::is_integral_v<T>)
170+
return ref.toInt();
171+
else if constexpr (std::is_floating_point_v<T>)
172+
return ref.toDouble();
173+
else if constexpr (std::is_same_v<T, QJsonValue>)
174+
return ref;
175+
else if constexpr (std::is_same_v<T, QJsonObject>)
176+
return ref.toObject();
177+
else if constexpr (std::is_same_v<T, QString>)
178+
return ref.toString();
179+
else if constexpr (std::is_same_v<T, QVariant>)
180+
return ref.toVariant();
181+
else
182+
throw(std::invalid_argument("Template argument for the TaggedJSONArray is not valid"));
183+
};
184+
};
185+
186+
/*!
187+
* \brief operator << stdout implementation for the contained object.
188+
* \param stream The std stream
189+
* \param obj The class instance
190+
* \return The output stream
191+
*/
192+
template<typename T>
193+
std::ostream& operator<< (std::ostream& stream, const TaggedJSONArray<T>& obj)
194+
{
195+
std::string text;
196+
for (const auto& curVal : obj.m_arr) {
197+
if constexpr (std::is_arithmetic_v<T>)
198+
text = std::to_string(curVal) + "\n";
199+
else
200+
text = QString(obj.dispatchValue(curVal)).toStdString() + "\n";
201+
stream << text;
202+
}
203+
return stream;
204+
};
205+
206+
207+
/*!
208+
* @brief Tagged object vector specialization of the TaggedJSONArray
209+
*
210+
* This object can be used for encapsulating JSON array of previously defined tagged object, which makes accessing them quite convenient to access.\n
211+
* Intended usage of this object is to use this class as member of the #TJO_DEFINE_JSON_TAGGED_OBJECT() macro.\n
212+
* This template specialization can only take the objects that has been defined by the #TJO_DEFINE_JSON_TAGGED_OBJECT() macro. Passing any other object may cause unintended side effects.
213+
* @tparam T
214+
*/
215+
template<typename T>
216+
class TaggedJSONArray<T, typename std::enable_if_t<TJO_IS_TAGGED_OBJECT>>
217+
{
218+
public:
219+
/*!
220+
* @brief Main constructor that is intended to be used with the #TJO_DEFINE_JSON_TAGGED_OBJECT() macro.
221+
* @param ref QJsonValue that holds the array of predefined JSON objects.
222+
* @param checkValue If set to true, invalid conversions (missing value, wrong type etc.) will throw a runtime error.
223+
*/
224+
explicit TaggedJSONArray(const QJsonValue& ref, const bool checkValue = true) : m_arr(extractFromQJSONArray<T>(ref.toArray(), checkValue))
225+
{
226+
//Check if there is a valid data if it's intended
227+
if (checkValue && ref.isUndefined())
228+
throw(std::runtime_error("Invalid data has been encountered while parsing the json data for TaggedJSONArray"));
229+
}
230+
231+
//!Assignment operator to get the whole container data
232+
template<typename V, typename = std::enable_if_t<std::is_convertible_v<V, std::vector<T>>>>
233+
void operator=(V&& val) { m_arr = std::forward<V>(val); };
234+
235+
bool operator!=(const std::vector<T>& other) const { return m_arr != other; };
236+
bool operator==(const std::vector<T>& other) const { return m_arr == other; };
237+
238+
//!Mutable reference of the stored object
239+
std::vector<T>& operator*() { return m_arr; };
240+
241+
//!Immutable reference of the stored object
242+
const std::vector<T>& operator*() const { return m_arr; };
243+
244+
//!Can be used for accessing the std::vector operations on the encapsulated data
245+
const std::vector<T>* operator->() const { return &m_arr; };
246+
247+
//!Mutable access operator
248+
T& operator[](const qsizetype i) { return m_arr[i]; };
249+
250+
//!Immutable access operator
251+
const T& at(const qsizetype i) const { return m_arr.at(i); };
252+
253+
//! Mutable access operator
254+
T& at(const qsizetype i) { return m_arr.at(i); };
255+
256+
//!QDebug enabler
257+
operator QString() const {
258+
QString ret;
259+
260+
for (const auto& curVal : m_arr)
261+
ret.append(QString(curVal) + "\n");
262+
return ret;
263+
};
264+
265+
//!QVector access
266+
QVector<T> toQVector() const { return QVector::fromStdVector(m_arr); }
267+
268+
private:
269+
std::vector<T> m_arr;
270+
};
271+
272+
using TaggedJSONBoolArray = TaggedJSONArray<bool>;
273+
using TaggedJSONIntArray = TaggedJSONArray<int>;
274+
using TaggedJSONDoubleArray = TaggedJSONArray<double>;
275+
using TaggedJSONValueArray = TaggedJSONArray<QJsonValue>;
276+
using TaggedQJsonObjectArray = TaggedJSONArray<QJsonObject>;
277+
using TaggedJSONStringArray = TaggedJSONArray<QString>;
278+
using TaggedJSONVariantArray = TaggedJSONArray<QVariant>;
279+
280+
#endif // TAGGEDJSONARRAY_H

0 commit comments

Comments
 (0)