GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: src/zserio/JsonParser.h Lines: 104 104 100.0 %
Date: 2023-12-13 14:51:09 Branches: 83 184 45.1 %

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