GCC Code Coverage Report | |||||||||||||||||||||
|
|||||||||||||||||||||
Line | Branch | Exec | Source |
1 |
#ifndef ZSERIO_JSON_PARSER_H_INC |
||
2 |
#define ZSERIO_JSON_PARSER_H_INC |
||
3 |
|||
4 |
#include "zserio/AnyHolder.h" |
||
5 |
#include "zserio/JsonDecoder.h" |
||
6 |
#include "zserio/JsonTokenizer.h" |
||
7 |
#include "zserio/Span.h" |
||
8 |
|||
9 |
namespace zserio |
||
10 |
{ |
||
11 |
|||
12 |
/** |
||
13 |
* Json Parser. |
||
14 |
* |
||
15 |
* Parses the JSON on the fly and calls an observer. |
||
16 |
*/ |
||
17 |
template <typename ALLOC = std::allocator<uint8_t>> |
||
18 |
85 |
class BasicJsonParser |
|
19 |
{ |
||
20 |
public: |
||
21 |
/** |
||
22 |
* Json Parser Observer. |
||
23 |
*/ |
||
24 |
117 |
class IObserver |
|
25 |
{ |
||
26 |
public: |
||
27 |
/** |
||
28 |
* Destructor. |
||
29 |
*/ |
||
30 |
✗✓✗✓ |
117 |
virtual ~IObserver() = default; |
31 |
|||
32 |
/** |
||
33 |
* Called when a JSON object begins - i.e. on '{'. |
||
34 |
*/ |
||
35 |
virtual void beginObject() = 0; |
||
36 |
|||
37 |
/** |
||
38 |
* Called when a JSON objects ends - i.e. on '}'. |
||
39 |
*/ |
||
40 |
virtual void endObject() = 0; |
||
41 |
|||
42 |
/** |
||
43 |
* Called when a JSON array begins - i.e. on '['. |
||
44 |
*/ |
||
45 |
virtual void beginArray() = 0; |
||
46 |
|||
47 |
/** |
||
48 |
* Called when a JSON array ends - i.e. on ']'. |
||
49 |
*/ |
||
50 |
virtual void endArray() = 0; |
||
51 |
|||
52 |
/** |
||
53 |
* Called on a JSON key. |
||
54 |
* |
||
55 |
* \param key String view to the key name. |
||
56 |
*/ |
||
57 |
virtual void visitKey(StringView key) = 0; |
||
58 |
|||
59 |
/** |
||
60 |
* Call on a JSON null value. |
||
61 |
* |
||
62 |
* \param nullValue Null value. |
||
63 |
*/ |
||
64 |
virtual void visitValue(std::nullptr_t nullValue) = 0; |
||
65 |
|||
66 |
/** |
||
67 |
* Call on a JSON bool value. |
||
68 |
* |
||
69 |
* \param boolValue Bool value. |
||
70 |
*/ |
||
71 |
virtual void visitValue(bool boolValue) = 0; |
||
72 |
|||
73 |
/** |
||
74 |
* Call on a JSON signed integer value. |
||
75 |
* |
||
76 |
* \param intValue Signed integer value. |
||
77 |
*/ |
||
78 |
virtual void visitValue(int64_t intValue) = 0; |
||
79 |
|||
80 |
/** |
||
81 |
* Call on a JSON unsigned integer value. |
||
82 |
* |
||
83 |
* \param uintValue Unsigned integer value. |
||
84 |
*/ |
||
85 |
virtual void visitValue(uint64_t uintValue) = 0; |
||
86 |
|||
87 |
/** |
||
88 |
* Call on a JSON floating point value. |
||
89 |
* |
||
90 |
* \param doubleValue Floating point value. |
||
91 |
*/ |
||
92 |
virtual void visitValue(double doubleValue) = 0; |
||
93 |
|||
94 |
/** |
||
95 |
* Call on a JSON string value. |
||
96 |
* |
||
97 |
* \param stringValue String view to the string value. |
||
98 |
*/ |
||
99 |
virtual void visitValue(StringView stringValue) = 0; |
||
100 |
}; |
||
101 |
|||
102 |
/** |
||
103 |
* Constructor. |
||
104 |
* |
||
105 |
* \param in Text stream to parse. |
||
106 |
* \param observer Observer to use. |
||
107 |
* \param allocator Allocator to use. |
||
108 |
*/ |
||
109 |
85 |
BasicJsonParser(std::istream& in, IObserver& observer, const ALLOC& allocator = ALLOC()) : |
|
110 |
85 |
m_tokenizer(in, allocator), m_observer(observer) |
|
111 |
85 |
{} |
|
112 |
|||
113 |
/** |
||
114 |
* Parses single JSON element from the text stream. |
||
115 |
* |
||
116 |
* \return True when end-of-file is reached, false otherwise (i.e. another JSON element is present). |
||
117 |
* \throw JsonParserException When parsing fails. |
||
118 |
*/ |
||
119 |
92 |
bool parse() |
|
120 |
{ |
||
121 |
✓✗✓✓ |
92 |
if (m_tokenizer.getToken() == JsonToken::BEGIN_OF_FILE) |
122 |
84 |
m_tokenizer.next(); |
|
123 |
|||
124 |
✗✓✓✓ |
92 |
if (m_tokenizer.getToken() == JsonToken::END_OF_FILE) |
125 |
1 |
return true; |
|
126 |
|||
127 |
91 |
parseElement(); |
|
128 |
|||
129 |
55 |
return m_tokenizer.getToken() == JsonToken::END_OF_FILE; |
|
130 |
} |
||
131 |
|||
132 |
/** |
||
133 |
* Gets current line number. |
||
134 |
* |
||
135 |
* \return Line number. |
||
136 |
*/ |
||
137 |
35 |
size_t getLine() const |
|
138 |
{ |
||
139 |
35 |
return m_tokenizer.getLine(); |
|
140 |
} |
||
141 |
|||
142 |
/** |
||
143 |
* Gets current column number. |
||
144 |
* |
||
145 |
* \return Column number. |
||
146 |
*/ |
||
147 |
35 |
size_t getColumn() const |
|
148 |
{ |
||
149 |
35 |
return m_tokenizer.getColumn(); |
|
150 |
} |
||
151 |
|||
152 |
private: |
||
153 |
void parseElement(); |
||
154 |
void parseObject(); |
||
155 |
void parseMembers(); |
||
156 |
void parseMember(); |
||
157 |
void parseArray(); |
||
158 |
void parseElements(); |
||
159 |
|||
160 |
void parseValue(); |
||
161 |
void visitValue() const; |
||
162 |
|||
163 |
void checkToken(JsonToken token); |
||
164 |
void consumeToken(JsonToken token); |
||
165 |
JsonParserException createUnexpectedTokenException(Span<const JsonToken> expecting) const; |
||
166 |
|||
167 |
static const std::array<JsonToken, 3> ELEMENT_TOKENS; |
||
168 |
|||
169 |
BasicJsonTokenizer<ALLOC> m_tokenizer; |
||
170 |
IObserver& m_observer; |
||
171 |
}; |
||
172 |
|||
173 |
template <typename ALLOC> |
||
174 |
const std::array<JsonToken, 3> BasicJsonParser<ALLOC>::ELEMENT_TOKENS = { |
||
175 |
JsonToken::BEGIN_OBJECT, |
||
176 |
JsonToken::BEGIN_ARRAY, |
||
177 |
JsonToken::VALUE |
||
178 |
}; |
||
179 |
|||
180 |
template <typename ALLOC> |
||
181 |
367 |
void BasicJsonParser<ALLOC>::parseElement() |
|
182 |
{ |
||
183 |
367 |
JsonToken token = m_tokenizer.getToken(); |
|
184 |
|||
185 |
✗✓✓✓ |
367 |
if (token == JsonToken::BEGIN_ARRAY) |
186 |
39 |
parseArray(); |
|
187 |
✓✓✓✓ |
328 |
else if (token == JsonToken::BEGIN_OBJECT) |
188 |
148 |
parseObject(); |
|
189 |
✓✗✓✓ |
180 |
else if (token == JsonToken::VALUE) |
190 |
179 |
parseValue(); |
|
191 |
else |
||
192 |
✗✗✓✗ |
1 |
throw createUnexpectedTokenException(ELEMENT_TOKENS); |
193 |
265 |
} |
|
194 |
|||
195 |
template <typename ALLOC> |
||
196 |
148 |
void BasicJsonParser<ALLOC>::parseObject() |
|
197 |
{ |
||
198 |
148 |
consumeToken(JsonToken::BEGIN_OBJECT); |
|
199 |
148 |
m_observer.beginObject(); |
|
200 |
|||
201 |
✓✗✓✓ |
146 |
if (m_tokenizer.getToken() == JsonToken::VALUE) |
202 |
144 |
parseMembers(); |
|
203 |
|||
204 |
80 |
consumeToken(JsonToken::END_OBJECT); |
|
205 |
78 |
m_observer.endObject(); |
|
206 |
77 |
} |
|
207 |
|||
208 |
template <typename ALLOC> |
||
209 |
144 |
void BasicJsonParser<ALLOC>::parseMembers() |
|
210 |
{ |
||
211 |
144 |
parseMember(); |
|
212 |
✗✓✓✓ |
212 |
while (m_tokenizer.getToken() == JsonToken::ITEM_SEPARATOR) |
213 |
{ |
||
214 |
74 |
m_tokenizer.next(); |
|
215 |
74 |
parseMember(); |
|
216 |
} |
||
217 |
78 |
} |
|
218 |
|||
219 |
template <typename ALLOC> |
||
220 |
218 |
void BasicJsonParser<ALLOC>::parseMember() |
|
221 |
{ |
||
222 |
218 |
checkToken(JsonToken::VALUE); |
|
223 |
217 |
const AnyHolder<ALLOC>& key = m_tokenizer.getValue(); |
|
224 |
✗✓✓✓ |
217 |
if (!key.template isType<string<ALLOC>>()) |
225 |
{ |
||
226 |
✗✗✗✗ ✗✗✗✗ ✗✗✗✗ ✗✗✓✗ ✓✗✓✗ ✓✗✓✗ ✓✗✓✗ |
2 |
throw JsonParserException("JsonParser:") << getLine() << ":" << getColumn() << |
227 |
2 |
": Key must be a string value!"; |
|
228 |
} |
||
229 |
✓✗✓✗ |
216 |
m_observer.visitKey(key.template get<string<ALLOC>>()); |
230 |
216 |
m_tokenizer.next(); |
|
231 |
|||
232 |
216 |
consumeToken(JsonToken::KEY_SEPARATOR); |
|
233 |
|||
234 |
214 |
parseElement(); |
|
235 |
152 |
} |
|
236 |
|||
237 |
template <typename ALLOC> |
||
238 |
39 |
void BasicJsonParser<ALLOC>::parseArray() |
|
239 |
{ |
||
240 |
39 |
consumeToken(JsonToken::BEGIN_ARRAY); |
|
241 |
39 |
m_observer.beginArray(); |
|
242 |
|||
243 |
✗✗✗✗ ✓✗✓✗ |
38 |
if (std::find(ELEMENT_TOKENS.begin(), ELEMENT_TOKENS.end(), m_tokenizer.getToken()) != ELEMENT_TOKENS.end()) |
244 |
38 |
parseElements(); |
|
245 |
|||
246 |
34 |
consumeToken(JsonToken::END_ARRAY); |
|
247 |
33 |
m_observer.endArray(); |
|
248 |
33 |
} |
|
249 |
|||
250 |
template <typename ALLOC> |
||
251 |
38 |
void BasicJsonParser<ALLOC>::parseElements() |
|
252 |
{ |
||
253 |
38 |
parseElement(); |
|
254 |
✗✗✓✓ |
82 |
while (m_tokenizer.getToken() == JsonToken::ITEM_SEPARATOR) |
255 |
{ |
||
256 |
24 |
m_tokenizer.next(); |
|
257 |
24 |
parseElement(); |
|
258 |
} |
||
259 |
34 |
} |
|
260 |
|||
261 |
template <typename ALLOC> |
||
262 |
179 |
void BasicJsonParser<ALLOC>::parseValue() |
|
263 |
{ |
||
264 |
179 |
visitValue(); |
|
265 |
155 |
m_tokenizer.next(); |
|
266 |
155 |
} |
|
267 |
|||
268 |
template <typename ALLOC> |
||
269 |
179 |
void BasicJsonParser<ALLOC>::visitValue() const |
|
270 |
{ |
||
271 |
179 |
const AnyHolder<ALLOC>& value = m_tokenizer.getValue(); |
|
272 |
|||
273 |
✗✓✓✓ |
179 |
if (value.template isType<std::nullptr_t>()) |
274 |
{ |
||
275 |
3 |
m_observer.visitValue(nullptr); |
|
276 |
} |
||
277 |
✗✓✓✓ |
176 |
else if (value.template isType<bool>()) |
278 |
{ |
||
279 |
3 |
m_observer.visitValue(value.template get<bool>()); |
|
280 |
} |
||
281 |
✗✓✓✓ |
173 |
else if (value.template isType<int64_t>()) |
282 |
{ |
||
283 |
4 |
m_observer.visitValue(value.template get<int64_t>()); |
|
284 |
} |
||
285 |
✗✓✓✓ |
169 |
else if (value.template isType<uint64_t>()) |
286 |
{ |
||
287 |
87 |
m_observer.visitValue(value.template get<uint64_t>()); |
|
288 |
} |
||
289 |
✗✓✓✓ |
82 |
else if (value.template isType<double>()) |
290 |
{ |
||
291 |
3 |
m_observer.visitValue(value.template get<double>()); |
|
292 |
} |
||
293 |
else |
||
294 |
{ |
||
295 |
✓✗✓✓ |
93 |
m_observer.visitValue(value.template get<string<ALLOC>>()); |
296 |
} |
||
297 |
155 |
} |
|
298 |
|||
299 |
template <typename ALLOC> |
||
300 |
735 |
void BasicJsonParser<ALLOC>::checkToken(JsonToken token) |
|
301 |
{ |
||
302 |
✗✓✓✓ |
735 |
if (m_tokenizer.getToken() != token) |
303 |
✗✗✓✗ |
5 |
throw createUnexpectedTokenException({{token}}); |
304 |
730 |
} |
|
305 |
|||
306 |
template <typename ALLOC> |
||
307 |
517 |
void BasicJsonParser<ALLOC>::consumeToken(JsonToken token) |
|
308 |
{ |
||
309 |
517 |
checkToken(token); |
|
310 |
513 |
m_tokenizer.next(); |
|
311 |
512 |
} |
|
312 |
|||
313 |
template <typename ALLOC> |
||
314 |
6 |
JsonParserException BasicJsonParser<ALLOC>::createUnexpectedTokenException( |
|
315 |
Span<const JsonToken> expecting) const |
||
316 |
{ |
||
317 |
6 |
JsonParserException error("JsonParser:"); |
|
318 |
✗✗✗✗ ✗✗✗✗ ✗✗✗✗ ✗✗✓✗ ✓✗✓✗ ✓✗✓✗ ✓✗✓✗ |
6 |
error << getLine() << ":" << getColumn() << ": unexpected token: " << m_tokenizer.getToken(); |
319 |
✗✗✓✓ |
6 |
if (expecting.size() == 1) |
320 |
{ |
||
321 |
✗✗✗✗ ✗✗✓✗ ✓✗✓✗ |
5 |
error << ", expecting " << expecting[0] << "!"; |
322 |
} |
||
323 |
else |
||
324 |
{ |
||
325 |
✗✗✓✗ |
1 |
error << ", expecting one of ["; |
326 |
✗✗✓✓ |
4 |
for (size_t i = 0; i < expecting.size(); ++i) |
327 |
{ |
||
328 |
✗✗✓✓ |
3 |
if (i > 0) |
329 |
✗✗✓✗ |
2 |
error << ", "; |
330 |
✗✗✓✗ |
3 |
error << expecting[i]; |
331 |
} |
||
332 |
✗✗✓✗ |
1 |
error << "]!"; |
333 |
} |
||
334 |
6 |
return error; |
|
335 |
} |
||
336 |
|||
337 |
/** Typedef to Json Parser provided for convenience - using default std::allocator<uint8_t>. */ |
||
338 |
using JsonParser = BasicJsonParser<>; |
||
339 |
|||
340 |
} // namespace |
||
341 |
|||
342 |
#endif // ZSERIO_JSON_PARSER_H_INC |
Generated by: GCOVR (Version 4.2) |