Coverage Report

Created: 2024-04-30 09:35

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
            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