/src/jsoncons/include/jsoncons/json_cursor.hpp
Line | Count | Source |
1 | | // Copyright 2013-2026 Daniel Parker |
2 | | // Distributed under the Boost license, Version 1.0. |
3 | | // (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) |
4 | | |
5 | | // See https://github.com/danielaparker/jsoncons for latest version |
6 | | |
7 | | #ifndef JSONCONS_JSON_CURSOR_HPP |
8 | | #define JSONCONS_JSON_CURSOR_HPP |
9 | | |
10 | | #include <cstddef> |
11 | | #include <functional> |
12 | | #include <ios> |
13 | | #include <memory> // std::allocator |
14 | | #include <system_error> |
15 | | |
16 | | #include <jsoncons/config/compiler_support.hpp> |
17 | | #include <jsoncons/utility/byte_string.hpp> |
18 | | #include <jsoncons/config/jsoncons_config.hpp> |
19 | | #include <jsoncons/conv_error.hpp> |
20 | | #include <jsoncons/json_exception.hpp> |
21 | | #include <jsoncons/json_parser.hpp> |
22 | | #include <jsoncons/json_visitor.hpp> |
23 | | #include <jsoncons/ser_utils.hpp> |
24 | | #include <jsoncons/source.hpp> |
25 | | #include <jsoncons/source_adaptor.hpp> |
26 | | #include <jsoncons/staj_cursor.hpp> |
27 | | #include <jsoncons/utility/unicode_traits.hpp> |
28 | | |
29 | | namespace jsoncons { |
30 | | |
31 | | template <typename CharT,typename Source=jsoncons::stream_source<CharT>,typename Allocator=std::allocator<char>> |
32 | | class basic_json_cursor : public basic_staj_cursor<CharT>, private virtual ser_context |
33 | | { |
34 | | public: |
35 | | using source_type = Source; |
36 | | using char_type = CharT; |
37 | | using allocator_type = Allocator; |
38 | | using string_view_type = jsoncons::basic_string_view<CharT>; |
39 | | private: |
40 | | using char_allocator_type = typename std::allocator_traits<allocator_type>:: template rebind_alloc<CharT>; |
41 | | static constexpr size_t default_max_buffer_size = 16384; |
42 | | |
43 | | json_source_adaptor<Source> source_; |
44 | | basic_json_parser<CharT,Allocator> parser_; |
45 | | basic_staj_visitor<CharT> cursor_visitor_; |
46 | | bool done_{false}; |
47 | | |
48 | | public: |
49 | | |
50 | | // Constructors that throw parse exceptions |
51 | | template <typename Sourceable> |
52 | | basic_json_cursor(Sourceable&& source, |
53 | | const basic_json_decode_options<CharT>& options = basic_json_decode_options<CharT>(), |
54 | | const Allocator& alloc = Allocator(), |
55 | | typename std::enable_if<!std::is_constructible<jsoncons::basic_string_view<CharT>,Sourceable>::value>::type* = 0) |
56 | | : source_(std::forward<Sourceable>(source)), |
57 | | parser_(options, alloc) |
58 | | { |
59 | | parser_.cursor_mode(true); |
60 | | if (!read_done()) |
61 | | { |
62 | | std::error_code local_ec; |
63 | | read_next(local_ec); |
64 | | if (local_ec) |
65 | | { |
66 | | if (local_ec == json_errc::unexpected_eof) |
67 | | { |
68 | | done_ = true; |
69 | | } |
70 | | else |
71 | | { |
72 | | JSONCONS_THROW(ser_error(local_ec, 1, 1)); |
73 | | } |
74 | | } |
75 | | } |
76 | | } |
77 | | template <typename Sourceable> |
78 | | basic_json_cursor(Sourceable&& source, |
79 | | const basic_json_decode_options<CharT>& options = basic_json_decode_options<CharT>(), |
80 | | const Allocator& alloc = Allocator(), |
81 | | typename std::enable_if<std::is_constructible<jsoncons::basic_string_view<CharT>,Sourceable>::value>::type* = 0) |
82 | | : source_(), |
83 | | parser_(options, alloc) |
84 | | { |
85 | | parser_.cursor_mode(true); |
86 | | initialize_with_string_view(std::forward<Sourceable>(source)); |
87 | | } |
88 | | |
89 | | #if !defined(JSONCONS_NO_DEPRECATED) |
90 | | template <typename Sourceable> |
91 | | basic_json_cursor(Sourceable&& source, |
92 | | const basic_json_decode_options<CharT>& options, |
93 | | std::function<bool(json_errc,const ser_context&)> err_handler, |
94 | | const Allocator& alloc = Allocator(), |
95 | | typename std::enable_if<!std::is_constructible<jsoncons::basic_string_view<CharT>,Sourceable>::value>::type* = 0) |
96 | | : source_(std::forward<Sourceable>(source)), |
97 | | parser_(options,err_handler,alloc) |
98 | | { |
99 | | parser_.cursor_mode(true); |
100 | | if (!read_done()) |
101 | | { |
102 | | std::error_code local_ec; |
103 | | read_next(local_ec); |
104 | | if (local_ec) |
105 | | { |
106 | | if (local_ec == json_errc::unexpected_eof) |
107 | | { |
108 | | done_ = true; |
109 | | } |
110 | | else |
111 | | { |
112 | | JSONCONS_THROW(ser_error(local_ec, 1, 1)); |
113 | | } |
114 | | } |
115 | | } |
116 | | } |
117 | | template <typename Sourceable> |
118 | | basic_json_cursor(Sourceable&& source, |
119 | | const basic_json_decode_options<CharT>& options, |
120 | | std::function<bool(json_errc,const ser_context&)> err_handler, |
121 | | const Allocator& alloc = Allocator(), |
122 | | typename std::enable_if<std::is_constructible<jsoncons::basic_string_view<CharT>,Sourceable>::value>::type* = 0) |
123 | | : source_(), |
124 | | parser_(options, err_handler, alloc) |
125 | | { |
126 | | parser_.cursor_mode(true); |
127 | | initialize_with_string_view(std::forward<Sourceable>(source)); |
128 | | } |
129 | | #endif |
130 | | |
131 | | // Constructors that set parse error codes |
132 | | template <typename Sourceable> |
133 | | basic_json_cursor(Sourceable&& source, std::error_code& ec) |
134 | 3.35k | : basic_json_cursor(std::allocator_arg, Allocator(), |
135 | 3.35k | std::forward<Sourceable>(source), |
136 | 3.35k | basic_json_decode_options<CharT>(), |
137 | 3.35k | ec) |
138 | 3.35k | { |
139 | 3.35k | } |
140 | | |
141 | | template <typename Sourceable> |
142 | | basic_json_cursor(Sourceable&& source, |
143 | | const basic_json_decode_options<CharT>& options, |
144 | | std::error_code& ec) |
145 | | : basic_json_cursor(std::allocator_arg, Allocator(), |
146 | | std::forward<Sourceable>(source), |
147 | | options, |
148 | | ec) |
149 | | { |
150 | | } |
151 | | |
152 | | #if !defined(JSONCONS_NO_DEPRECATED) |
153 | | template <typename Sourceable> |
154 | | basic_json_cursor(Sourceable&& source, |
155 | | const basic_json_decode_options<CharT>& options, |
156 | | std::function<bool(json_errc,const ser_context&)> err_handler, |
157 | | std::error_code& ec) |
158 | | : basic_json_cursor(std::allocator_arg, Allocator(), |
159 | | std::forward<Sourceable>(source), |
160 | | options, |
161 | | err_handler, |
162 | | ec) |
163 | | { |
164 | | } |
165 | | |
166 | | template <typename Sourceable> |
167 | | basic_json_cursor(std::allocator_arg_t, const Allocator& alloc, |
168 | | Sourceable&& source, |
169 | | const basic_json_decode_options<CharT>& options, |
170 | | std::function<bool(json_errc,const ser_context&)> err_handler, |
171 | | std::error_code& ec, |
172 | | typename std::enable_if<!std::is_constructible<jsoncons::basic_string_view<CharT>,Sourceable>::value>::type* = 0) |
173 | | : source_(std::forward<Sourceable>(source)), |
174 | | parser_(options,err_handler,alloc) |
175 | | { |
176 | | parser_.cursor_mode(true); |
177 | | |
178 | | if (!read_done()) |
179 | | { |
180 | | std::error_code local_ec; |
181 | | read_next(local_ec); |
182 | | if (local_ec) |
183 | | { |
184 | | if (local_ec == json_errc::unexpected_eof) |
185 | | { |
186 | | done_ = true; |
187 | | } |
188 | | else |
189 | | { |
190 | | ec = local_ec; |
191 | | } |
192 | | } |
193 | | } |
194 | | } |
195 | | #endif |
196 | | |
197 | | template <typename Sourceable> |
198 | | basic_json_cursor(std::allocator_arg_t, const Allocator& alloc, |
199 | | Sourceable&& source, |
200 | | const basic_json_decode_options<CharT>& options, |
201 | | std::error_code& ec, |
202 | | typename std::enable_if<!std::is_constructible<jsoncons::basic_string_view<CharT>,Sourceable>::value>::type* = 0) |
203 | 3.35k | : source_(std::forward<Sourceable>(source)), |
204 | 3.35k | parser_(options, alloc) |
205 | 3.35k | { |
206 | 3.35k | parser_.cursor_mode(true); |
207 | | |
208 | 3.35k | if (!read_done()) |
209 | 3.35k | { |
210 | 3.35k | std::error_code local_ec; |
211 | 3.35k | read_next(local_ec); |
212 | 3.35k | if (local_ec) |
213 | 1.34k | { |
214 | 1.34k | if (local_ec == json_errc::unexpected_eof) |
215 | 644 | { |
216 | 644 | done_ = true; |
217 | 644 | } |
218 | 704 | else |
219 | 704 | { |
220 | 704 | ec = local_ec; |
221 | 704 | } |
222 | 1.34k | } |
223 | 3.35k | } |
224 | 3.35k | } |
225 | | #if !defined(JSONCONS_NO_DEPRECATED) |
226 | | |
227 | | template <typename Sourceable> |
228 | | basic_json_cursor(std::allocator_arg_t, const Allocator& alloc, |
229 | | Sourceable&& source, |
230 | | const basic_json_decode_options<CharT>& options, |
231 | | std::function<bool(json_errc,const ser_context&)> err_handler, |
232 | | std::error_code& ec, |
233 | | typename std::enable_if<std::is_constructible<jsoncons::basic_string_view<CharT>,Sourceable>::value>::type* = 0) |
234 | | : source_(), |
235 | | parser_(options, err_handler, alloc) |
236 | | { |
237 | | parser_.cursor_mode(true); |
238 | | initialize_with_string_view(std::forward<Sourceable>(source), ec); |
239 | | } |
240 | | #endif |
241 | | template <typename Sourceable> |
242 | | basic_json_cursor(std::allocator_arg_t, const Allocator& alloc, |
243 | | Sourceable&& source, |
244 | | const basic_json_decode_options<CharT>& options, |
245 | | std::error_code& ec, |
246 | | typename std::enable_if<std::is_constructible<jsoncons::basic_string_view<CharT>,Sourceable>::value>::type* = 0) |
247 | | : source_(), |
248 | | parser_(options, alloc) |
249 | | { |
250 | | parser_.cursor_mode(true); |
251 | | initialize_with_string_view(std::forward<Sourceable>(source), ec); |
252 | | } |
253 | | |
254 | | basic_json_cursor(const basic_json_cursor&) = delete; |
255 | | basic_json_cursor(basic_json_cursor&&) = default; |
256 | | |
257 | 3.35k | ~basic_json_cursor() = default; |
258 | | |
259 | | // Noncopyable and nonmoveable |
260 | | |
261 | | basic_json_cursor& operator=(const basic_json_cursor&) = delete; |
262 | | basic_json_cursor& operator=(basic_json_cursor&&) = default; |
263 | | |
264 | | void reset() |
265 | | { |
266 | | parser_.reset(); |
267 | | cursor_visitor_.reset(); |
268 | | done_ = false; |
269 | | if (!read_done()) |
270 | | { |
271 | | read_next(); |
272 | | } |
273 | | } |
274 | | |
275 | | template <typename Sourceable> |
276 | | typename std::enable_if<!std::is_constructible<jsoncons::basic_string_view<CharT>,Sourceable>::value>::type |
277 | | reset(Sourceable&& source) |
278 | | { |
279 | | source_ = std::forward<Sourceable>(source); |
280 | | parser_.reinitialize(); |
281 | | cursor_visitor_.reset(); |
282 | | done_ = false; |
283 | | if (!read_done()) |
284 | | { |
285 | | read_next(); |
286 | | } |
287 | | } |
288 | | |
289 | | template <typename Sourceable> |
290 | | typename std::enable_if<std::is_constructible<jsoncons::basic_string_view<CharT>,Sourceable>::value>::type |
291 | | reset(Sourceable&& source) |
292 | | { |
293 | | source_ = {}; |
294 | | parser_.reinitialize(); |
295 | | cursor_visitor_.reset(); |
296 | | done_ = false; |
297 | | initialize_with_string_view(std::forward<Sourceable>(source)); |
298 | | } |
299 | | |
300 | | void reset(std::error_code& ec) |
301 | | { |
302 | | parser_.reset(); |
303 | | cursor_visitor_.reset(); |
304 | | done_ = false; |
305 | | if (!read_done()) |
306 | | { |
307 | | read_next(ec); |
308 | | } |
309 | | } |
310 | | |
311 | | template <typename Sourceable> |
312 | | typename std::enable_if<!std::is_constructible<jsoncons::basic_string_view<CharT>,Sourceable>::value>::type |
313 | | reset(Sourceable&& source, std::error_code& ec) |
314 | | { |
315 | | source_ = std::forward<Sourceable>(source); |
316 | | parser_.reinitialize(); |
317 | | cursor_visitor_.reset(); |
318 | | done_ = false; |
319 | | if (!read_done()) |
320 | | { |
321 | | read_next(ec); |
322 | | } |
323 | | } |
324 | | |
325 | | template <typename Sourceable> |
326 | | typename std::enable_if<std::is_constructible<jsoncons::basic_string_view<CharT>,Sourceable>::value>::type |
327 | | reset(Sourceable&& source, std::error_code& ec) |
328 | | { |
329 | | source_ = {}; |
330 | | parser_.reinitialize(); |
331 | | done_ = false; |
332 | | initialize_with_string_view(std::forward<Sourceable>(source), ec); |
333 | | } |
334 | | |
335 | | bool done() const override |
336 | 5.34k | { |
337 | 5.34k | return parser_.done() || done_; |
338 | 5.34k | } |
339 | | |
340 | | const basic_staj_event<CharT>& current() const override |
341 | 1.98k | { |
342 | 1.98k | return cursor_visitor_.event(); |
343 | 1.98k | } |
344 | | |
345 | | void read_to(basic_json_visitor<CharT>& visitor) override |
346 | 0 | { |
347 | 0 | std::error_code ec; |
348 | 0 | read_to(visitor, ec); |
349 | 0 | if (JSONCONS_UNLIKELY(ec)) |
350 | 0 | { |
351 | 0 | JSONCONS_THROW(ser_error(ec,parser_.line(),parser_.column())); |
352 | 0 | } |
353 | 0 | } |
354 | | |
355 | | void read_to(basic_json_visitor<CharT>& visitor, |
356 | | std::error_code& ec) override |
357 | 0 | { |
358 | 0 | if (is_begin_container(current().event_type())) |
359 | 0 | { |
360 | 0 | parser_.cursor_mode(false); |
361 | 0 | parser_.mark_level(parser_.level()); |
362 | 0 | cursor_visitor_.event().send_json_event(visitor, *this, ec); |
363 | 0 | if (JSONCONS_UNLIKELY(ec)) |
364 | 0 | { |
365 | 0 | return; |
366 | 0 | } |
367 | 0 | read_next(visitor, ec); |
368 | 0 | parser_.cursor_mode(true); |
369 | 0 | parser_.mark_level(0); |
370 | 0 | if (current().event_type() == staj_events::begin_object) |
371 | 0 | { |
372 | 0 | cursor_visitor_.end_object(*this); |
373 | 0 | } |
374 | 0 | else |
375 | 0 | { |
376 | 0 | cursor_visitor_.end_array(*this); |
377 | 0 | } |
378 | 0 | } |
379 | 0 | else |
380 | 0 | { |
381 | 0 | cursor_visitor_.event().send_json_event(visitor, *this, ec); |
382 | 0 | } |
383 | 0 | } |
384 | | |
385 | | void next() override |
386 | 0 | { |
387 | 0 | read_next(); |
388 | 0 | } |
389 | | |
390 | | void next(std::error_code& ec) override |
391 | 1.98k | { |
392 | 1.98k | read_next(ec); |
393 | 1.98k | } |
394 | | |
395 | | void check_done() |
396 | | { |
397 | | std::error_code ec; |
398 | | check_done(ec); |
399 | | if (JSONCONS_UNLIKELY(ec)) |
400 | | { |
401 | | JSONCONS_THROW(ser_error(ec,parser_.line(),parser_.column())); |
402 | | } |
403 | | } |
404 | | |
405 | | const ser_context& context() const override |
406 | 0 | { |
407 | 0 | return *this; |
408 | 0 | } |
409 | | |
410 | | void check_done(std::error_code& ec) |
411 | | { |
412 | | if (source_.is_error()) |
413 | | { |
414 | | ec = json_errc::source_error; |
415 | | return; |
416 | | } |
417 | | if (source_.eof()) |
418 | | { |
419 | | parser_.check_done(ec); |
420 | | if (JSONCONS_UNLIKELY(ec)) {return;} |
421 | | } |
422 | | else |
423 | | { |
424 | | do |
425 | | { |
426 | | if (parser_.source_exhausted()) |
427 | | { |
428 | | auto s = source_.read_buffer(ec); |
429 | | if (JSONCONS_UNLIKELY(ec)) {return;} |
430 | | if (s.size() > 0) |
431 | | { |
432 | | parser_.update(s.data(),s.size()); |
433 | | } |
434 | | } |
435 | | if (!parser_.source_exhausted()) |
436 | | { |
437 | | parser_.check_done(ec); |
438 | | if (JSONCONS_UNLIKELY(ec)) {return;} |
439 | | } |
440 | | } |
441 | | while (!eof()); |
442 | | } |
443 | | } |
444 | | |
445 | | bool eof() const |
446 | | { |
447 | | return parser_.source_exhausted() && source_.eof(); |
448 | | } |
449 | | |
450 | | std::size_t line() const override |
451 | 0 | { |
452 | 0 | return parser_.line(); |
453 | 0 | } |
454 | | |
455 | | std::size_t column() const override |
456 | 0 | { |
457 | 0 | return parser_.column(); |
458 | 0 | } |
459 | | |
460 | | friend |
461 | | basic_staj_filter_view<CharT> operator|(basic_json_cursor& cursor, |
462 | | std::function<bool(const basic_staj_event<CharT>&, const ser_context&)> pred) |
463 | | { |
464 | | return basic_staj_filter_view<CharT>(cursor, pred); |
465 | | } |
466 | | |
467 | | private: |
468 | | |
469 | | bool read_done() const |
470 | 3.35k | { |
471 | 3.35k | return parser_.done() || done_; |
472 | 3.35k | } |
473 | | |
474 | | void initialize_with_string_view(string_view_type sv) |
475 | | { |
476 | | std::error_code local_ec; |
477 | | initialize_with_string_view(sv, local_ec); |
478 | | if (local_ec) |
479 | | { |
480 | | JSONCONS_THROW(ser_error(local_ec, 1, 1)); |
481 | | } |
482 | | } |
483 | | |
484 | | void initialize_with_string_view(string_view_type sv, std::error_code& ec) |
485 | | { |
486 | | auto r = unicode_traits::detect_json_encoding(sv.data(), sv.size()); |
487 | | if (!(r.encoding == unicode_traits::encoding_kind::utf8 || r.encoding == unicode_traits::encoding_kind::undetected)) |
488 | | { |
489 | | ec = json_errc::illegal_unicode_character; |
490 | | return; |
491 | | } |
492 | | std::size_t offset = (r.ptr - sv.data()); |
493 | | parser_.update(sv.data()+offset,sv.size()-offset); |
494 | | bool read_done = parser_.done() || done_; |
495 | | if (!read_done) |
496 | | { |
497 | | std::error_code local_ec; |
498 | | read_next(local_ec); |
499 | | if (local_ec) |
500 | | { |
501 | | if (local_ec == json_errc::unexpected_eof) |
502 | | { |
503 | | done_ = true; |
504 | | } |
505 | | else |
506 | | { |
507 | | ec = local_ec; |
508 | | } |
509 | | } |
510 | | } |
511 | | } |
512 | | |
513 | | void read_next() |
514 | 0 | { |
515 | 0 | std::error_code ec; |
516 | 0 | read_next(cursor_visitor_, ec); |
517 | 0 | if (JSONCONS_UNLIKELY(ec)) |
518 | 0 | { |
519 | 0 | JSONCONS_THROW(ser_error(ec,parser_.line(),parser_.column())); |
520 | 0 | } |
521 | 0 | } |
522 | | |
523 | | void read_next(std::error_code& ec) |
524 | 5.34k | { |
525 | 5.34k | read_next(cursor_visitor_, ec); |
526 | 5.34k | } |
527 | | |
528 | | void read_next(basic_json_visitor<CharT>& visitor, std::error_code& ec) |
529 | 5.34k | { |
530 | 5.34k | parser_.restart(); |
531 | 15.4k | while (!parser_.stopped()) |
532 | 11.4k | { |
533 | 11.4k | if (parser_.source_exhausted()) |
534 | 11.3k | { |
535 | 11.3k | auto s = source_.read_buffer(ec); |
536 | 11.3k | if (JSONCONS_UNLIKELY(ec)) {return;} |
537 | 11.3k | if (s.size() > 0) |
538 | 6.99k | { |
539 | 6.99k | parser_.update(s.data(),s.size()); |
540 | 6.99k | if (JSONCONS_UNLIKELY(ec)) {return;} |
541 | 6.99k | } |
542 | 11.3k | } |
543 | 11.4k | bool eof = parser_.source_exhausted() && source_.eof(); |
544 | 11.4k | parser_.parse_some(visitor, ec); |
545 | 11.4k | if (JSONCONS_UNLIKELY(ec)) {return;} |
546 | 10.1k | if (eof) |
547 | 3.74k | { |
548 | 3.74k | if (parser_.enter()) |
549 | 25 | { |
550 | 25 | done_ = true; |
551 | 25 | break; |
552 | 25 | } |
553 | 3.72k | else if (!parser_.accept()) |
554 | 9 | { |
555 | 9 | ec = json_errc::unexpected_eof; |
556 | 9 | return; |
557 | 9 | } |
558 | 3.74k | } |
559 | 10.1k | } |
560 | 5.34k | } |
561 | | }; |
562 | | |
563 | | using json_stream_cursor = basic_json_cursor<char,jsoncons::stream_source<char>>; |
564 | | using json_string_cursor = basic_json_cursor<char,jsoncons::string_source<char>>; |
565 | | using wjson_stream_cursor = basic_json_cursor<wchar_t,jsoncons::stream_source<wchar_t>>; |
566 | | using wjson_string_cursor = basic_json_cursor<wchar_t,jsoncons::string_source<wchar_t>>; |
567 | | |
568 | | } // namespace jsoncons |
569 | | |
570 | | #endif // JSONCONS_JSON_CURSOR_HPP |
571 | | |