1 | // Copyright (C) 2021 The Qt Company Ltd. |
---|---|
2 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only |
3 | |
4 | #include "qtypedjson_p.h" |
5 | #include <QtCore/QLoggingCategory> |
6 | #include <QtCore/qjsondocument.h> |
7 | #include <QtCore/qjsonvalue.h> |
8 | #include <QtCore/qjsonarray.h> |
9 | #include <cstring> |
10 | |
11 | QT_BEGIN_NAMESPACE |
12 | |
13 | namespace QTypedJson { |
14 | |
15 | Q_LOGGING_CATEGORY(jsonRpcLog, "qt.jsonrpc"); |
16 | |
17 | Reader::Reader(const QJsonValue &v) |
18 | : m_p(new ReaderPrivate { .valuesStack: QList({ ValueStack { .value: v, .fieldPath: QString(), .indexPath: -1, .warnLevel: 0 } }) }) |
19 | { |
20 | } |
21 | |
22 | Reader::~Reader() |
23 | { |
24 | for (const QString &msg : m_p->errorMessages) |
25 | qCWarning(jsonRpcLog) << msg; |
26 | delete m_p; |
27 | } |
28 | |
29 | QStringList Reader::errorMessages() |
30 | { |
31 | return m_p->errorMessages; |
32 | } |
33 | |
34 | void Reader::clearErrorMessages() |
35 | { |
36 | m_p->errorMessages.clear(); |
37 | } |
38 | |
39 | void Reader::handleBasic(bool &el) |
40 | { |
41 | if (currentValue().isBool()) |
42 | el = currentValue().toBool(); |
43 | else |
44 | warnMissing(s: u"bool"); |
45 | } |
46 | |
47 | void Reader::handleBasic(QByteArray &el) |
48 | { |
49 | if (currentValue().isString()) |
50 | el = currentValue().toString().toUtf8(); |
51 | else |
52 | warnMissing(s: u"string"); |
53 | } |
54 | |
55 | void Reader::handleBasic(int &el) |
56 | { |
57 | if (currentValue().isDouble()) |
58 | el = currentValue().toInt(defaultValue: el); |
59 | else |
60 | warnMissing(s: u"int"); |
61 | } |
62 | |
63 | void Reader::handleBasic(double &el) |
64 | { |
65 | if (currentValue().isDouble()) |
66 | el = currentValue().toDouble(); |
67 | else |
68 | warnMissing(s: u"double"); |
69 | } |
70 | |
71 | void Reader::handleNullType() |
72 | { |
73 | if (!currentValue().isNull() && !currentValue().isUndefined()) { |
74 | warnNonNull(); |
75 | } |
76 | } |
77 | |
78 | bool Reader::startField(const QString &fieldName) |
79 | { |
80 | int oldWarnLevel = (m_p->valuesStack.isEmpty() ? 0 : m_p->valuesStack.last().warnLevel); |
81 | m_p->objectsStack.last().visitedFields.insert(value: fieldName); |
82 | m_p->valuesStack.append(t: ValueStack { .value: currentValue()[fieldName], .fieldPath: fieldName, .indexPath: -1, |
83 | .warnLevel: (oldWarnLevel ? oldWarnLevel + 1 : 0) }); |
84 | return true; |
85 | } |
86 | |
87 | bool Reader::startField(const char *fieldName) |
88 | { |
89 | QString f = QString::fromUtf8(utf8: fieldName); // conversion needed just to set the fieldPath |
90 | return startField(fieldName: f); |
91 | } |
92 | |
93 | void Reader::endField(const QString &fieldName) |
94 | { |
95 | Q_ASSERT(m_p->valuesStack.last().fieldPath == fieldName); |
96 | m_p->valuesStack.removeLast(); |
97 | } |
98 | |
99 | void Reader::endField(const char *fieldName) |
100 | { |
101 | QString f = QString::fromUtf8(utf8: fieldName); |
102 | endField(fieldName: f); |
103 | } |
104 | |
105 | bool Reader::startObjectF(const char *type, ObjectOptions options, quintptr) |
106 | { |
107 | if (m_p->parseStatus != ParseStatus::Normal) |
108 | return false; |
109 | if (currentValue().isUndefined()) { |
110 | m_p->parseStatus = ParseStatus::Failed; |
111 | return false; |
112 | } |
113 | m_p->objectsStack.append(t: ObjectStack { .type: type, .options: options, .visitedFields: {} }); |
114 | return true; |
115 | } |
116 | |
117 | void Reader::endObjectF(const char *type, ObjectOptions, quintptr) |
118 | { |
119 | Q_ASSERT(std::strcmp(m_p->objectsStack.last().type, type) == 0); |
120 | m_p->objectsStack.removeLast(); |
121 | } |
122 | |
123 | void Reader::warnExtra(const QJsonObject &e) |
124 | { |
125 | if (e.constBegin() != e.constEnd()) |
126 | warn(QStringLiteral(u"%1 has extra fields %2") |
127 | .arg(args: currentPath(), args: QString::fromUtf8(ba: QJsonDocument(e).toJson()))); |
128 | } |
129 | |
130 | void Reader::warnInvalidSize(qint32 size, qint32 expectedSize) |
131 | { |
132 | if (size != expectedSize) |
133 | warn(QStringLiteral(u"%1 expected %1 elements, not %2.") |
134 | .arg(args: currentPath(), args: QString::number(expectedSize), args: QString::number(size))); |
135 | } |
136 | |
137 | void Reader::warnMissing(QStringView s) |
138 | { |
139 | warn(QStringLiteral(u"%1 misses value of type %2").arg(args: currentPath(), args&: s)); |
140 | } |
141 | |
142 | void Reader::warnNonNull() |
143 | { |
144 | QByteArray val = QJsonDocument(QJsonArray({ currentValue() })).toJson(); |
145 | warn(QStringLiteral(u"%1 is supposed to be null, but is %2") |
146 | .arg(args: currentPath(), args: QString::fromUtf8(ba: val.mid(index: 1, len: val.size() - 2)))); |
147 | } |
148 | |
149 | void Reader::warn(const QString &msg) |
150 | { |
151 | m_p->errorMessages.append(t: msg); |
152 | m_p->parseStatus = ParseStatus::Failed; |
153 | } |
154 | |
155 | void Reader::handleJson(QJsonValue &v) |
156 | { |
157 | v = currentValue(); |
158 | } |
159 | |
160 | void Reader::handleJson(QJsonObject &v) |
161 | { |
162 | if (!currentValue().isObject() && !currentValue().isNull() && !currentValue().isUndefined()) { |
163 | QByteArray val = QJsonDocument(QJsonArray({ currentValue() })).toJson(); |
164 | warn(QStringLiteral(u"Error: expected an object at %1, not %2") |
165 | .arg(args: currentPath(), args: QString::fromUtf8(ba: val.mid(index: 1, len: val.size() - 2)))); |
166 | } |
167 | v = currentValue().toObject(); |
168 | } |
169 | |
170 | void Reader::handleJson(QJsonArray &v) |
171 | { |
172 | if (!currentValue().isArray() && !currentValue().isNull() && !currentValue().isUndefined()) { |
173 | QByteArray val = QJsonDocument(QJsonArray({ currentValue() })).toJson(); |
174 | warn(QStringLiteral(u"Error: expected an array at %1, not %2") |
175 | .arg(args: currentPath(), args: QString::fromUtf8(ba: val.mid(index: 1, len: val.size() - 2)))); |
176 | } |
177 | v = currentValue().toArray(); |
178 | } |
179 | |
180 | QJsonObject Reader::getExtraFields() const |
181 | { |
182 | QJsonObject extraFields; |
183 | QJsonObject v = currentValue().toObject(); |
184 | auto it = v.constBegin(); |
185 | auto end = v.constEnd(); |
186 | auto &vField = m_p->objectsStack.last().visitedFields; |
187 | while (it != end) { |
188 | if (!vField.contains(value: it.key())) { |
189 | extraFields.insert(key: it.key(), value: it.value()); |
190 | } |
191 | ++it; |
192 | } |
193 | return extraFields; |
194 | } |
195 | |
196 | void Reader::startArrayF(qint32 &size) |
197 | { |
198 | size = int(currentValue().toArray().size()); |
199 | } |
200 | |
201 | bool Reader::startElement(qint32 index) |
202 | { |
203 | int oldWarnLevel = (m_p->valuesStack.isEmpty() ? 0 : m_p->valuesStack.last().warnLevel); |
204 | m_p->valuesStack.append(t: ValueStack { .value: currentValue().toArray().at(i: index), .fieldPath: QString(), .indexPath: index, |
205 | .warnLevel: (oldWarnLevel ? oldWarnLevel + 1 : 0) }); |
206 | return true; |
207 | } |
208 | |
209 | void Reader::endElement(qint32 index) |
210 | { |
211 | Q_ASSERT(m_p->valuesStack.last().indexPath == index); |
212 | m_p->valuesStack.removeLast(); |
213 | } |
214 | |
215 | void Reader::endArrayF(qint32 &) { } |
216 | |
217 | QString Reader::currentPath() const |
218 | { |
219 | QStringList res; |
220 | for (const auto &el : std::as_const(t&: m_p->valuesStack)) { |
221 | if (el.indexPath != -1) |
222 | res.append(t: QString::number(el.indexPath)); |
223 | else |
224 | res.append(t: el.fieldPath); |
225 | } |
226 | return res.join(sep: u"."); |
227 | } |
228 | |
229 | bool Reader::startTuple(qint32 size) |
230 | { |
231 | qint32 expected = qint32(currentValue().toArray().size()); |
232 | if (size != expected) { |
233 | warnInvalidSize(size, expectedSize: expected); |
234 | return false; |
235 | }; |
236 | return true; |
237 | } |
238 | |
239 | void Reader::endTuple(qint32) { } |
240 | |
241 | void JsonBuilder::handleBasic(const bool &v) |
242 | { |
243 | m_values.append(t: QJsonValue(v)); |
244 | } |
245 | |
246 | void JsonBuilder::handleBasic(const QByteArray &v) |
247 | { |
248 | m_values.append(t: QJsonValue(QString::fromUtf8(ba: v))); |
249 | } |
250 | |
251 | void JsonBuilder::handleBasic(const int &v) |
252 | { |
253 | m_values.append(t: QJsonValue(v)); |
254 | } |
255 | |
256 | void JsonBuilder::handleBasic(const double &v) |
257 | { |
258 | m_values.append(t: QJsonValue(v)); |
259 | } |
260 | |
261 | void JsonBuilder::handleNullType() |
262 | { |
263 | m_values.append(t: QJsonValue(QJsonValue::Type::Null)); |
264 | } |
265 | |
266 | void JsonBuilder::handleMissingOptional() |
267 | { |
268 | if (m_fieldLevel.isEmpty() || m_fieldLevel.last() != m_values.size()) |
269 | handleNullType(); |
270 | } |
271 | |
272 | bool JsonBuilder::startField(const QString &) |
273 | { |
274 | m_fieldLevel.append(t: m_values.size()); |
275 | return true; |
276 | } |
277 | |
278 | bool JsonBuilder::startField(const char *) |
279 | { |
280 | m_fieldLevel.append(t: m_values.size()); |
281 | return true; |
282 | } |
283 | |
284 | void JsonBuilder::endField(const QString &v) |
285 | { |
286 | Q_ASSERT(!m_fieldLevel.isEmpty()); |
287 | if (m_fieldLevel.last() < m_values.size()) { |
288 | Q_ASSERT(m_values.size() > 1); |
289 | if (QJsonObject *o = std::get_if<QJsonObject>(ptr: &m_values[m_values.size() - 2])) { |
290 | o->insert(key: v, value: popLastValue()); |
291 | } else { |
292 | Q_ASSERT(false); |
293 | } |
294 | } |
295 | Q_ASSERT(!m_fieldLevel.isEmpty() && m_fieldLevel.last() == m_values.size()); |
296 | m_fieldLevel.removeLast(); |
297 | } |
298 | |
299 | void JsonBuilder::endField(const char *v) |
300 | { |
301 | endField(v: QString::fromUtf8(utf8: v)); |
302 | } |
303 | |
304 | bool JsonBuilder::startObjectF(const char *, ObjectOptions, quintptr) |
305 | { |
306 | m_values.append(t: QJsonObject()); |
307 | return true; |
308 | } |
309 | |
310 | void JsonBuilder::endObjectF(const char *, ObjectOptions, quintptr) { } |
311 | |
312 | bool JsonBuilder::startArrayF(qint32 &) |
313 | { |
314 | m_values.append(t: QJsonArray()); |
315 | m_arrayLevel.append(t: m_values.size()); |
316 | return true; |
317 | } |
318 | |
319 | bool JsonBuilder::startElement(qint32) |
320 | { |
321 | return true; |
322 | } |
323 | |
324 | void JsonBuilder::endElement(qint32) |
325 | { |
326 | Q_ASSERT(m_values.size() > 1); |
327 | if (QJsonArray *a = std::get_if<QJsonArray>(ptr: &m_values[m_values.size() - 2])) { |
328 | a->append(value: popLastValue()); |
329 | } else { |
330 | Q_ASSERT(false); |
331 | } |
332 | } |
333 | |
334 | void JsonBuilder::endArrayF(qint32 &) |
335 | { |
336 | Q_ASSERT(!m_arrayLevel.isEmpty() && m_arrayLevel.last() == m_values.size()); |
337 | m_arrayLevel.removeLast(); |
338 | } |
339 | |
340 | void JsonBuilder::handleJson(QJsonValue &v) |
341 | { |
342 | m_values.append(t: v); |
343 | } |
344 | |
345 | void JsonBuilder::handleJson(QJsonObject &v) |
346 | { |
347 | m_values.append(t: v); |
348 | } |
349 | |
350 | void JsonBuilder::handleJson(QJsonArray &v) |
351 | { |
352 | m_values.append(t: v); |
353 | } |
354 | |
355 | QJsonValue JsonBuilder::popLastValue() |
356 | { |
357 | if (m_values.isEmpty()) |
358 | return QJsonValue(QJsonValue::Type::Undefined); |
359 | QJsonValue res = std::visit(visitor: [](auto &v) { return QJsonValue(v); }, variants&: m_values.last()); |
360 | m_values.removeLast(); |
361 | return res; |
362 | } |
363 | |
364 | bool JsonBuilder::startTuple(qint32 size) |
365 | { |
366 | return startArrayF(size); |
367 | } |
368 | |
369 | void JsonBuilder::endTuple(qint32 size) |
370 | { |
371 | endArrayF(size); |
372 | } |
373 | |
374 | } // namespace QTypedJson |
375 | |
376 | QT_END_NAMESPACE |
377 |
Definitions
- jsonRpcLog
- Reader
- ~Reader
- errorMessages
- clearErrorMessages
- handleBasic
- handleBasic
- handleBasic
- handleBasic
- handleNullType
- startField
- startField
- endField
- endField
- startObjectF
- endObjectF
- warnExtra
- warnInvalidSize
- warnMissing
- warnNonNull
- warn
- handleJson
- handleJson
- handleJson
- getExtraFields
- startArrayF
- startElement
- endElement
- endArrayF
- currentPath
- startTuple
- endTuple
- handleBasic
- handleBasic
- handleBasic
- handleBasic
- handleNullType
- handleMissingOptional
- startField
- startField
- endField
- endField
- startObjectF
- endObjectF
- startArrayF
- startElement
- endElement
- endArrayF
- handleJson
- handleJson
- handleJson
- popLastValue
- startTuple
Start learning QML with our Intro Training
Find out more