Coverage Report

Created: 2024-07-18 11:41

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