Line | Count | 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 | | class BasicJsonParser |
19 | | { |
20 | | public: |
21 | | /** |
22 | | * Json Parser Observer. |
23 | | */ |
24 | | class IObserver |
25 | | { |
26 | | public: |
27 | | /** |
28 | | * Destructor. |
29 | | */ |
30 | 123 | 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 | | BasicJsonParser(std::istream& in, IObserver& observer, const ALLOC& allocator = ALLOC()) : |
110 | | m_tokenizer(in, allocator), |
111 | | m_observer(observer) |
112 | 87 | {} |
113 | | |
114 | | /** |
115 | | * Parses single JSON element from the text stream. |
116 | | * |
117 | | * \return True when end-of-file is reached, false otherwise (i.e. another JSON element is present). |
118 | | * \throw JsonParserException When parsing fails. |
119 | | */ |
120 | | bool parse() |
121 | 94 | { |
122 | 94 | if (m_tokenizer.getToken() == JsonToken::BEGIN_OF_FILE) |
123 | 86 | m_tokenizer.next(); |
124 | | |
125 | 94 | if (m_tokenizer.getToken() == JsonToken::END_OF_FILE) |
126 | 1 | return true; |
127 | | |
128 | 93 | parseElement(); |
129 | | |
130 | 93 | return m_tokenizer.getToken() == JsonToken::END_OF_FILE; |
131 | 94 | } |
132 | | |
133 | | /** |
134 | | * Gets current line number. |
135 | | * |
136 | | * \return Line number. |
137 | | */ |
138 | | size_t getLine() const |
139 | 35 | { |
140 | 35 | return m_tokenizer.getLine(); |
141 | 35 | } |
142 | | |
143 | | /** |
144 | | * Gets current column number. |
145 | | * |
146 | | * \return Column number. |
147 | | */ |
148 | | size_t getColumn() const |
149 | 35 | { |
150 | 35 | return m_tokenizer.getColumn(); |
151 | 35 | } |
152 | | |
153 | | private: |
154 | | void parseElement(); |
155 | | void parseObject(); |
156 | | void parseMembers(); |
157 | | void parseMember(); |
158 | | void parseArray(); |
159 | | void parseElements(); |
160 | | |
161 | | void parseValue(); |
162 | | void visitValue() const; |
163 | | |
164 | | void checkToken(JsonToken token); |
165 | | void consumeToken(JsonToken token); |
166 | | JsonParserException createUnexpectedTokenException(Span<const JsonToken> expecting) const; |
167 | | |
168 | | static const std::array<JsonToken, 3> ELEMENT_TOKENS; |
169 | | |
170 | | BasicJsonTokenizer<ALLOC> m_tokenizer; |
171 | | IObserver& m_observer; |
172 | | }; |
173 | | |
174 | | template <typename ALLOC> |
175 | | const std::array<JsonToken, 3> BasicJsonParser<ALLOC>::ELEMENT_TOKENS = { |
176 | | JsonToken::BEGIN_OBJECT, JsonToken::BEGIN_ARRAY, JsonToken::VALUE}; |
177 | | |
178 | | template <typename ALLOC> |
179 | | void BasicJsonParser<ALLOC>::parseElement() |
180 | 393 | { |
181 | 393 | JsonToken token = m_tokenizer.getToken(); |
182 | | |
183 | 393 | if (token == JsonToken::BEGIN_ARRAY) |
184 | 43 | parseArray(); |
185 | 350 | else if (token == JsonToken::BEGIN_OBJECT) |
186 | 156 | parseObject(); |
187 | 194 | else if (token == JsonToken::VALUE) |
188 | 193 | parseValue(); |
189 | 1 | else |
190 | 1 | throw createUnexpectedTokenException(ELEMENT_TOKENS); |
191 | 393 | } |
192 | | |
193 | | template <typename ALLOC> |
194 | | void BasicJsonParser<ALLOC>::parseObject() |
195 | 156 | { |
196 | 156 | consumeToken(JsonToken::BEGIN_OBJECT); |
197 | 156 | m_observer.beginObject(); |
198 | | |
199 | 156 | if (m_tokenizer.getToken() == JsonToken::VALUE) |
200 | 152 | parseMembers(); |
201 | | |
202 | 156 | consumeToken(JsonToken::END_OBJECT); |
203 | 156 | m_observer.endObject(); |
204 | 156 | } |
205 | | |
206 | | template <typename ALLOC> |
207 | | void BasicJsonParser<ALLOC>::parseMembers() |
208 | 152 | { |
209 | 152 | parseMember(); |
210 | 240 | while (m_tokenizer.getToken() == JsonToken::ITEM_SEPARATOR) |
211 | 88 | { |
212 | 88 | m_tokenizer.next(); |
213 | 88 | parseMember(); |
214 | 88 | } |
215 | 152 | } |
216 | | |
217 | | template <typename ALLOC> |
218 | | void BasicJsonParser<ALLOC>::parseMember() |
219 | 240 | { |
220 | 240 | checkToken(JsonToken::VALUE); |
221 | 240 | const AnyHolder<ALLOC>& key = m_tokenizer.getValue(); |
222 | 240 | if (!key.template isType<string<ALLOC>>()) |
223 | 1 | { |
224 | 1 | throw JsonParserException("JsonParser:") |
225 | 1 | << getLine() << ":" << getColumn() << ": Key must be a string value!"; |
226 | 1 | } |
227 | 239 | m_observer.visitKey(key.template get<string<ALLOC>>()); |
228 | 239 | m_tokenizer.next(); |
229 | | |
230 | 239 | consumeToken(JsonToken::KEY_SEPARATOR); |
231 | | |
232 | 239 | parseElement(); |
233 | 239 | } |
234 | | |
235 | | template <typename ALLOC> |
236 | | void BasicJsonParser<ALLOC>::parseArray() |
237 | 43 | { |
238 | 43 | consumeToken(JsonToken::BEGIN_ARRAY); |
239 | 43 | m_observer.beginArray(); |
240 | | |
241 | 43 | if (std::find(ELEMENT_TOKENS.begin(), ELEMENT_TOKENS.end(), m_tokenizer.getToken()) != ELEMENT_TOKENS.end()) |
242 | 39 | parseElements(); |
243 | | |
244 | 43 | consumeToken(JsonToken::END_ARRAY); |
245 | 43 | m_observer.endArray(); |
246 | 43 | } |
247 | | |
248 | | template <typename ALLOC> |
249 | | void BasicJsonParser<ALLOC>::parseElements() |
250 | 39 | { |
251 | 39 | parseElement(); |
252 | 64 | while (m_tokenizer.getToken() == JsonToken::ITEM_SEPARATOR) |
253 | 25 | { |
254 | 25 | m_tokenizer.next(); |
255 | 25 | parseElement(); |
256 | 25 | } |
257 | 39 | } |
258 | | |
259 | | template <typename ALLOC> |
260 | | void BasicJsonParser<ALLOC>::parseValue() |
261 | 193 | { |
262 | 193 | visitValue(); |
263 | 193 | m_tokenizer.next(); |
264 | 193 | } |
265 | | |
266 | | template <typename ALLOC> |
267 | | void BasicJsonParser<ALLOC>::visitValue() const |
268 | 193 | { |
269 | 193 | const AnyHolder<ALLOC>& value = m_tokenizer.getValue(); |
270 | | |
271 | 193 | if (value.template isType<std::nullptr_t>()) |
272 | 3 | { |
273 | 3 | m_observer.visitValue(nullptr); |
274 | 3 | } |
275 | 190 | else if (value.template isType<bool>()) |
276 | 3 | { |
277 | 3 | m_observer.visitValue(value.template get<bool>()); |
278 | 3 | } |
279 | 187 | else if (value.template isType<int64_t>()) |
280 | 6 | { |
281 | 6 | m_observer.visitValue(value.template get<int64_t>()); |
282 | 6 | } |
283 | 181 | else if (value.template isType<uint64_t>()) |
284 | 97 | { |
285 | 97 | m_observer.visitValue(value.template get<uint64_t>()); |
286 | 97 | } |
287 | 84 | else if (value.template isType<double>()) |
288 | 3 | { |
289 | 3 | m_observer.visitValue(value.template get<double>()); |
290 | 3 | } |
291 | 81 | else |
292 | 81 | { |
293 | 81 | m_observer.visitValue(value.template get<string<ALLOC>>()); |
294 | 81 | } |
295 | 193 | } |
296 | | |
297 | | template <typename ALLOC> |
298 | | void BasicJsonParser<ALLOC>::checkToken(JsonToken token) |
299 | 803 | { |
300 | 803 | if (m_tokenizer.getToken() != token) |
301 | 5 | throw createUnexpectedTokenException({{token}}); |
302 | 803 | } |
303 | | |
304 | | template <typename ALLOC> |
305 | | void BasicJsonParser<ALLOC>::consumeToken(JsonToken token) |
306 | 563 | { |
307 | 563 | checkToken(token); |
308 | 563 | m_tokenizer.next(); |
309 | 563 | } |
310 | | |
311 | | template <typename ALLOC> |
312 | | JsonParserException BasicJsonParser<ALLOC>::createUnexpectedTokenException( |
313 | | Span<const JsonToken> expecting) const |
314 | 6 | { |
315 | 6 | JsonParserException error("JsonParser:"); |
316 | 6 | error << getLine() << ":" << getColumn() << ": unexpected token: " << m_tokenizer.getToken(); |
317 | 6 | if (expecting.size() == 1) |
318 | 5 | { |
319 | 5 | error << ", expecting " << expecting[0] << "!"; |
320 | 5 | } |
321 | 1 | else |
322 | 1 | { |
323 | 1 | error << ", expecting one of ["; |
324 | 4 | for (size_t i = 0; i < expecting.size(); ++i3 ) |
325 | 3 | { |
326 | 3 | if (i > 0) |
327 | 2 | error << ", "; |
328 | 3 | error << expecting[i]; |
329 | 3 | } |
330 | 1 | error << "]!"; |
331 | 1 | } |
332 | 6 | return error; |
333 | 6 | } |
334 | | |
335 | | /** Typedef to Json Parser provided for convenience - using default std::allocator<uint8_t>. */ |
336 | | using JsonParser = BasicJsonParser<>; |
337 | | |
338 | | } // namespace zserio |
339 | | |
340 | | #endif // ZSERIO_JSON_PARSER_H_INC |