MythTV master
mythjsonserialiser.cpp
Go to the documentation of this file.
1// Qt
2#include <QChar> // Fix Qt6 GCC SFINAE warning
3#include <QMetaProperty>
4#if QT_VERSION < QT_VERSION_CHECK(6,11,0)
5#include <QSequentialIterable>
6#else
7#include <QMetaSequence>
8#endif
9
10// MythTV
11#include "mythdate.h"
12#include "http/mythhttpdata.h"
14
15MythJSONSerialiser::MythJSONSerialiser(const QString& Name, const QVariant& Value)
16{
17 m_first.push(true);
18 m_writer.setDevice(&m_buffer);
19 QString name = Name;
20 if (name.startsWith("V2"))
21 name.remove(0,2);
22 AddObject(name, Value);
23 m_writer.flush();
24}
25
26void MythJSONSerialiser::AddObject(const QString& Name, const QVariant& Value)
27{
28 if (!m_first.top())
29 m_writer << ",";
30 m_writer << "{\"" << Name << "\": ";
31 AddValue(Value);
32 m_writer << "}";
33 m_first.top() = false;
34}
35
36void MythJSONSerialiser::AddValue(const QVariant& Value, const QMetaProperty *MetaProperty)
37{
38 if (Value.isNull() || !Value.isValid())
39 {
40 m_writer << "null";
41 return;
42 }
43
44 auto * object = Value.value<QObject*>();
45 if (object)
46 {
47 QVariant isNull = object->property("isNull");
48 if (isNull.value<bool>())
49 {
50 m_writer << "null";
51 return;
52 }
53 AddQObject(object);
54 return;
55 }
56
57 // Enum ?
58 if (MetaProperty && (MetaProperty->isEnumType() || MetaProperty->isFlagType()))
59 {
60 QMetaEnum metaEnum = MetaProperty->enumerator();
61 QString value = MetaProperty->isFlagType() ? metaEnum.valueToKeys(Value.toInt()).constData() :
62 metaEnum.valueToKey(Value.toInt());
63 // If couldn't convert to enum name, return raw value
64 value = (value.isEmpty() ? Value.toString() : value);
65 m_writer << "\"" << Encode(value) << "\"";
66 return;
67 }
68
69 switch (
70#if QT_VERSION < QT_VERSION_CHECK(6,0,0)
71 static_cast<QMetaType::Type>(Value.type())
72#else
73 static_cast<QMetaType::Type>(Value.typeId())
74#endif
75 )
76 {
77 case QMetaType::Int:
78 case QMetaType::UInt:
79 case QMetaType::LongLong:
80 case QMetaType::ULongLong:
81 m_writer << Value.toString();
82 break;
83 case QMetaType::Double:
84 m_writer << QString::number(Value.toDouble(),'f',6);
85 break;
86 case QMetaType::Float:
87 m_writer << QString::number(Value.toFloat(),'f',6);
88 break;
89 case QMetaType::Bool: m_writer << (Value.toBool() ? "true" : "false"); break;
90 case QMetaType::QStringList: AddStringList(Value); break;
91 case QMetaType::QVariantList: AddList(Value); break;
92 case QMetaType::QVariantMap: AddMap(Value.toMap()); break;
93 case QMetaType::QDateTime: m_writer << "\"" << Encode(MythDate::toString(Value.toDateTime(), MythDate::ISODate)) << "\""; break;
94 default:
95 m_writer << "\"" << Encode(Value.toString()) << "\"";
96 }
97}
98
99void MythJSONSerialiser::AddQObject(const QObject* Object)
100{
101 if (!Object)
102 return;
103 const auto * metaobject = Object->metaObject();
104 int count = metaobject->propertyCount();
105 m_first.push(true);
106 QString first;
107 m_writer << "{";
108 for (int index = 0; index < count; ++index )
109 {
110 QMetaProperty metaProperty = metaobject->property(index);
111 if (
112#if QT_VERSION < QT_VERSION_CHECK(6,0,0)
113 metaProperty.isUser(Object)
114#else
115 metaProperty.isUser()
116#endif
117 )
118 {
119 const char *rawname = metaProperty.name();
120 QString name(rawname);
121 if (name.compare("objectName") == 0)
122 continue;
123 QVariant value(Object->property(rawname));
124 m_writer << first << "\"" << name << "\": ";
125 AddValue(value, &metaProperty);
126 first = ", ";
127 }
128 }
129 m_writer << "}";
130 m_first.pop();
131}
132
133void MythJSONSerialiser::AddStringList(const QVariant &Values)
134{
135 QString first;
136 m_writer << "[";
137#if QT_VERSION < QT_VERSION_CHECK(6,11,0)
138 auto values = Values.value<QSequentialIterable>();
139#else
140 auto values = Values.value<QMetaSequence::Iterable>();
141#endif
142 for (const auto & value : values)
143 {
144 m_writer << first << "\"" << Encode(value.toString()) << "\"";
145 first = ",";
146 }
147 m_writer << "]";
148}
149
150void MythJSONSerialiser::AddList(const QVariant& Values)
151{
152 m_first.push(true);
153 QString first;
154 m_writer << "[";
155#if QT_VERSION < QT_VERSION_CHECK(6,11,0)
156 auto values = Values.value<QSequentialIterable>();
157#else
158 auto values = Values.value<QMetaSequence::Iterable>();
159#endif
160 for (const auto & value : values)
161 {
162 m_writer << first;
163 AddValue(value);
164 first = ",";
165 }
166 m_writer << "]";
167 m_first.pop();
168}
169
170void MythJSONSerialiser::AddMap(const QVariantMap& Map)
171{
172 m_first.push(true);
173 QString first;
174 m_writer << "{";
175 for (auto it = Map.cbegin(); it != Map.cend(); ++it)
176 {
177 m_writer << first << "\"" << it.key() << "\":";
178 AddValue(it.value());
179 first = ",";
180 }
181 m_writer << "}";
182 m_first.pop();
183}
184
185QString MythJSONSerialiser::Encode(const QString& Value)
186{
187 if (Value.isEmpty())
188 return Value;
189
190 QString value = Value;
191 value.replace( '\\', "\\\\" ); // This must be first
192 value.replace( '"' , "\\\"" );
193 value.replace( '\b', "\\b" ); // ^H (\u0008)
194 value.replace( '\f', "\\f" ); // ^L (\u000C)
195 value.replace( '\n', "\\n" ); // ^J (\u000A)
196 value.replace( "\r", "\\r" ); // ^M (\u000D)
197 value.replace( "\t", "\\t" ); // ^I (\u0009)
198 value.replace( "/", "\\/" );
199
200 // Escape remaining chars from \u0000 - \u001F
201 // Details at https://en.wikipedia.org/wiki/C0_and_C1_control_codes
202 value.replace(QChar('\u0000'), "\\u0000"); // ^@ NULL
203 value.replace(QChar('\u0001'), "\\u0001"); // ^A
204 value.replace(QChar('\u0002'), "\\u0002"); // ^B
205 value.replace(QChar('\u0003'), "\\u0003"); // ^C
206 value.replace(QChar('\u0004'), "\\u0004"); // ^D
207 value.replace(QChar('\u0005'), "\\u0005"); // ^E
208 value.replace(QChar('\u0006'), "\\u0006"); // ^F
209 value.replace(QChar('\u0007'), "\\u0007"); // ^G (\a)
210 value.replace(QChar('\u000B'), "\\u000B"); // ^K (\v)
211 value.replace(QChar('\u000E'), "\\u000E"); // ^N
212 value.replace(QChar('\u000F'), "\\u000F"); // ^O
213 value.replace(QChar('\u0010'), "\\u0010"); // ^P
214 value.replace(QChar('\u0011'), "\\u0011"); // ^Q XON
215 value.replace(QChar('\u0012'), "\\u0012"); // ^R
216 value.replace(QChar('\u0013'), "\\u0013"); // ^S XOFF
217 value.replace(QChar('\u0014'), "\\u0014"); // ^T
218 value.replace(QChar('\u0015'), "\\u0015"); // ^U
219 value.replace(QChar('\u0016'), "\\u0016"); // ^V
220 value.replace(QChar('\u0017'), "\\u0017"); // ^W
221 value.replace(QChar('\u0018'), "\\u0018"); // ^X
222 value.replace(QChar('\u0019'), "\\u0019"); // ^Y
223 value.replace(QChar('\u001A'), "\\u001A"); // ^Z
224 value.replace(QChar('\u001B'), "\\u001B");
225 value.replace(QChar('\u001C'), "\\u001C");
226 value.replace(QChar('\u001D'), "\\u001D");
227 value.replace(QChar('\u001E'), "\\u001E");
228 value.replace(QChar('\u001F'), "\\u001F");
229
230 return value;
231}
void AddObject(const QString &Name, const QVariant &Value)
std::stack< bool > m_first
void AddMap(const QVariantMap &Map)
void AddQObject(const QObject *Object)
void AddValue(const QVariant &Value, const QMetaProperty *MetaProperty=nullptr)
void AddStringList(const QVariant &Values)
MythJSONSerialiser(const QString &Name, const QVariant &Value)
static QString Encode(const QString &Value)
void AddList(const QVariant &Values)
QString toString(const QDateTime &raw_dt, uint format)
Returns formatted string representing the time.
Definition: mythdate.cpp:93
@ ISODate
Default UTC.
Definition: mythdate.h:18