Branch data Line data Source code
1 : : #pragma once
2 : :
3 : : #include <cassert> // assert
4 : : #include <cstddef>
5 : : #include <string> // string
6 : : #include <utility> // move
7 : : #include <vector> // vector
8 : :
9 : : #include <nlohmann/detail/exceptions.hpp>
10 : : #include <nlohmann/detail/macro_scope.hpp>
11 : :
12 : : namespace nlohmann
13 : : {
14 : :
15 : : /*!
16 : : @brief SAX interface
17 : :
18 : : This class describes the SAX interface used by @ref nlohmann::json::sax_parse.
19 : : Each function is called in different situations while the input is parsed. The
20 : : boolean return value informs the parser whether to continue processing the
21 : : input.
22 : : */
23 : : template<typename BasicJsonType>
24 : : struct json_sax
25 : : {
26 : : /// type for (signed) integers
27 : : using number_integer_t = typename BasicJsonType::number_integer_t;
28 : : /// type for unsigned integers
29 : : using number_unsigned_t = typename BasicJsonType::number_unsigned_t;
30 : : /// type for floating-point numbers
31 : : using number_float_t = typename BasicJsonType::number_float_t;
32 : : /// type for strings
33 : : using string_t = typename BasicJsonType::string_t;
34 : :
35 : : /*!
36 : : @brief a null value was read
37 : : @return whether parsing should proceed
38 : : */
39 : : virtual bool null() = 0;
40 : :
41 : : /*!
42 : : @brief a boolean value was read
43 : : @param[in] val boolean value
44 : : @return whether parsing should proceed
45 : : */
46 : : virtual bool boolean(bool val) = 0;
47 : :
48 : : /*!
49 : : @brief an integer number was read
50 : : @param[in] val integer value
51 : : @return whether parsing should proceed
52 : : */
53 : : virtual bool number_integer(number_integer_t val) = 0;
54 : :
55 : : /*!
56 : : @brief an unsigned integer number was read
57 : : @param[in] val unsigned integer value
58 : : @return whether parsing should proceed
59 : : */
60 : : virtual bool number_unsigned(number_unsigned_t val) = 0;
61 : :
62 : : /*!
63 : : @brief an floating-point number was read
64 : : @param[in] val floating-point value
65 : : @param[in] s raw token value
66 : : @return whether parsing should proceed
67 : : */
68 : : virtual bool number_float(number_float_t val, const string_t& s) = 0;
69 : :
70 : : /*!
71 : : @brief a string was read
72 : : @param[in] val string value
73 : : @return whether parsing should proceed
74 : : @note It is safe to move the passed string.
75 : : */
76 : : virtual bool string(string_t& val) = 0;
77 : :
78 : : /*!
79 : : @brief the beginning of an object was read
80 : : @param[in] elements number of object elements or -1 if unknown
81 : : @return whether parsing should proceed
82 : : @note binary formats may report the number of elements
83 : : */
84 : : virtual bool start_object(std::size_t elements) = 0;
85 : :
86 : : /*!
87 : : @brief an object key was read
88 : : @param[in] val object key
89 : : @return whether parsing should proceed
90 : : @note It is safe to move the passed string.
91 : : */
92 : : virtual bool key(string_t& val) = 0;
93 : :
94 : : /*!
95 : : @brief the end of an object was read
96 : : @return whether parsing should proceed
97 : : */
98 : : virtual bool end_object() = 0;
99 : :
100 : : /*!
101 : : @brief the beginning of an array was read
102 : : @param[in] elements number of array elements or -1 if unknown
103 : : @return whether parsing should proceed
104 : : @note binary formats may report the number of elements
105 : : */
106 : : virtual bool start_array(std::size_t elements) = 0;
107 : :
108 : : /*!
109 : : @brief the end of an array was read
110 : : @return whether parsing should proceed
111 : : */
112 : : virtual bool end_array() = 0;
113 : :
114 : : /*!
115 : : @brief a parse error occurred
116 : : @param[in] position the position in the input where the error occurs
117 : : @param[in] last_token the last read token
118 : : @param[in] ex an exception object describing the error
119 : : @return whether parsing should proceed (must return false)
120 : : */
121 : : virtual bool parse_error(std::size_t position,
122 : : const std::string& last_token,
123 : : const detail::exception& ex) = 0;
124 : :
125 : : virtual ~json_sax() = default;
126 : : };
127 : :
128 : :
129 : : namespace detail
130 : : {
131 : : /*!
132 : : @brief SAX implementation to create a JSON value from SAX events
133 : :
134 : : This class implements the @ref json_sax interface and processes the SAX events
135 : : to create a JSON value which makes it basically a DOM parser. The structure or
136 : : hierarchy of the JSON value is managed by the stack `ref_stack` which contains
137 : : a pointer to the respective array or object for each recursion depth.
138 : :
139 : : After successful parsing, the value that is passed by reference to the
140 : : constructor contains the parsed value.
141 : :
142 : : @tparam BasicJsonType the JSON type
143 : : */
144 : : template<typename BasicJsonType>
145 : : class json_sax_dom_parser
146 : : {
147 : : public:
148 : : using number_integer_t = typename BasicJsonType::number_integer_t;
149 : : using number_unsigned_t = typename BasicJsonType::number_unsigned_t;
150 : : using number_float_t = typename BasicJsonType::number_float_t;
151 : : using string_t = typename BasicJsonType::string_t;
152 : :
153 : : /*!
154 : : @param[in, out] r reference to a JSON value that is manipulated while
155 : : parsing
156 : : @param[in] allow_exceptions_ whether parse errors yield exceptions
157 : : */
158 : 0 : explicit json_sax_dom_parser(BasicJsonType& r, const bool allow_exceptions_ = true)
159 : 0 : : root(r), allow_exceptions(allow_exceptions_)
160 : 0 : {}
161 : :
162 : : // make class move-only
163 : : json_sax_dom_parser(const json_sax_dom_parser&) = delete;
164 : : json_sax_dom_parser(json_sax_dom_parser&&) = default;
165 : : json_sax_dom_parser& operator=(const json_sax_dom_parser&) = delete;
166 : : json_sax_dom_parser& operator=(json_sax_dom_parser&&) = default;
167 : 0 : ~json_sax_dom_parser() = default;
168 : :
169 : 0 : bool null()
170 : : {
171 : 0 : handle_value(nullptr);
172 : 0 : return true;
173 : : }
174 : :
175 : 0 : bool boolean(bool val)
176 : : {
177 : 0 : handle_value(val);
178 : 0 : return true;
179 : : }
180 : :
181 : 0 : bool number_integer(number_integer_t val)
182 : : {
183 : 0 : handle_value(val);
184 : 0 : return true;
185 : : }
186 : :
187 : 0 : bool number_unsigned(number_unsigned_t val)
188 : : {
189 : 0 : handle_value(val);
190 : 0 : return true;
191 : : }
192 : :
193 : 0 : bool number_float(number_float_t val, const string_t& /*unused*/)
194 : : {
195 : 0 : handle_value(val);
196 : 0 : return true;
197 : : }
198 : :
199 : 0 : bool string(string_t& val)
200 : : {
201 : 0 : handle_value(val);
202 : 0 : return true;
203 : : }
204 : :
205 : 0 : bool start_object(std::size_t len)
206 : : {
207 : 0 : ref_stack.push_back(handle_value(BasicJsonType::value_t::object));
208 : :
209 : 0 : if (JSON_UNLIKELY(len != std::size_t(-1) and len > ref_stack.back()->max_size()))
210 : : {
211 : 0 : JSON_THROW(out_of_range::create(408,
212 : : "excessive object size: " + std::to_string(len)));
213 : : }
214 : :
215 : 0 : return true;
216 : 0 : }
217 : :
218 : 0 : bool key(string_t& val)
219 : : {
220 : : // add null at given key and store the reference for later
221 : 0 : object_element = &(ref_stack.back()->m_value.object->operator[](val));
222 : 0 : return true;
223 : : }
224 : :
225 : 0 : bool end_object()
226 : : {
227 : 0 : ref_stack.pop_back();
228 : 0 : return true;
229 : : }
230 : :
231 : 0 : bool start_array(std::size_t len)
232 : : {
233 : 0 : ref_stack.push_back(handle_value(BasicJsonType::value_t::array));
234 : :
235 : 0 : if (JSON_UNLIKELY(len != std::size_t(-1) and len > ref_stack.back()->max_size()))
236 : : {
237 : 0 : JSON_THROW(out_of_range::create(408,
238 : : "excessive array size: " + std::to_string(len)));
239 : : }
240 : :
241 : 0 : return true;
242 : 0 : }
243 : :
244 : 0 : bool end_array()
245 : : {
246 : 0 : ref_stack.pop_back();
247 : 0 : return true;
248 : : }
249 : :
250 : 0 : bool parse_error(std::size_t /*unused*/, const std::string& /*unused*/,
251 : : const detail::exception& ex)
252 : : {
253 : 0 : errored = true;
254 : 0 : if (allow_exceptions)
255 : : {
256 : : // determine the proper exception type from the id
257 : 0 : switch ((ex.id / 100) % 100)
258 : : {
259 : : case 1:
260 : 0 : JSON_THROW(*static_cast<const detail::parse_error*>(&ex));
261 : : case 4:
262 : 0 : JSON_THROW(*static_cast<const detail::out_of_range*>(&ex));
263 : : // LCOV_EXCL_START
264 : : case 2:
265 : : JSON_THROW(*static_cast<const detail::invalid_iterator*>(&ex));
266 : : case 3:
267 : : JSON_THROW(*static_cast<const detail::type_error*>(&ex));
268 : : case 5:
269 : : JSON_THROW(*static_cast<const detail::other_error*>(&ex));
270 : : default:
271 : : assert(false);
272 : : // LCOV_EXCL_STOP
273 : : }
274 : : }
275 : 0 : return false;
276 : : }
277 : :
278 : 0 : constexpr bool is_errored() const
279 : : {
280 : 0 : return errored;
281 : : }
282 : :
283 : : private:
284 : : /*!
285 : : @invariant If the ref stack is empty, then the passed value will be the new
286 : : root.
287 : : @invariant If the ref stack contains a value, then it is an array or an
288 : : object to which we can add elements
289 : : */
290 : : template<typename Value>
291 : 0 : BasicJsonType* handle_value(Value&& v)
292 : : {
293 : 0 : if (ref_stack.empty())
294 : : {
295 : 0 : root = BasicJsonType(std::forward<Value>(v));
296 : 0 : return &root;
297 : : }
298 : :
299 : 0 : assert(ref_stack.back()->is_array() or ref_stack.back()->is_object());
300 : :
301 : 0 : if (ref_stack.back()->is_array())
302 : : {
303 : 0 : ref_stack.back()->m_value.array->emplace_back(std::forward<Value>(v));
304 : 0 : return &(ref_stack.back()->m_value.array->back());
305 : : }
306 : :
307 : 0 : assert(ref_stack.back()->is_object());
308 : 0 : assert(object_element);
309 : 0 : *object_element = BasicJsonType(std::forward<Value>(v));
310 : 0 : return object_element;
311 : 0 : }
312 : :
313 : : /// the parsed JSON value
314 : : BasicJsonType& root;
315 : : /// stack to model hierarchy of values
316 : 0 : std::vector<BasicJsonType*> ref_stack {};
317 : : /// helper to hold the reference for the next object element
318 : 0 : BasicJsonType* object_element = nullptr;
319 : : /// whether a syntax error occurred
320 : 0 : bool errored = false;
321 : : /// whether to throw exceptions in case of errors
322 : : const bool allow_exceptions = true;
323 : : };
324 : :
325 : : template<typename BasicJsonType>
326 : : class json_sax_dom_callback_parser
327 : : {
328 : : public:
329 : : using number_integer_t = typename BasicJsonType::number_integer_t;
330 : : using number_unsigned_t = typename BasicJsonType::number_unsigned_t;
331 : : using number_float_t = typename BasicJsonType::number_float_t;
332 : : using string_t = typename BasicJsonType::string_t;
333 : : using parser_callback_t = typename BasicJsonType::parser_callback_t;
334 : : using parse_event_t = typename BasicJsonType::parse_event_t;
335 : :
336 : 0 : json_sax_dom_callback_parser(BasicJsonType& r,
337 : : const parser_callback_t cb,
338 : : const bool allow_exceptions_ = true)
339 : 0 : : root(r), callback(cb), allow_exceptions(allow_exceptions_)
340 : : {
341 : 0 : keep_stack.push_back(true);
342 : 0 : }
343 : :
344 : : // make class move-only
345 : : json_sax_dom_callback_parser(const json_sax_dom_callback_parser&) = delete;
346 : : json_sax_dom_callback_parser(json_sax_dom_callback_parser&&) = default;
347 : : json_sax_dom_callback_parser& operator=(const json_sax_dom_callback_parser&) = delete;
348 : : json_sax_dom_callback_parser& operator=(json_sax_dom_callback_parser&&) = default;
349 : 0 : ~json_sax_dom_callback_parser() = default;
350 : :
351 : 0 : bool null()
352 : : {
353 : 0 : handle_value(nullptr);
354 : 0 : return true;
355 : : }
356 : :
357 : 0 : bool boolean(bool val)
358 : : {
359 : 0 : handle_value(val);
360 : 0 : return true;
361 : : }
362 : :
363 : 0 : bool number_integer(number_integer_t val)
364 : : {
365 : 0 : handle_value(val);
366 : 0 : return true;
367 : : }
368 : :
369 : 0 : bool number_unsigned(number_unsigned_t val)
370 : : {
371 : 0 : handle_value(val);
372 : 0 : return true;
373 : : }
374 : :
375 : 0 : bool number_float(number_float_t val, const string_t& /*unused*/)
376 : : {
377 : 0 : handle_value(val);
378 : 0 : return true;
379 : : }
380 : :
381 : 0 : bool string(string_t& val)
382 : : {
383 : 0 : handle_value(val);
384 : 0 : return true;
385 : : }
386 : :
387 : 0 : bool start_object(std::size_t len)
388 : : {
389 : : // check callback for object start
390 : 0 : const bool keep = callback(static_cast<int>(ref_stack.size()), parse_event_t::object_start, discarded);
391 : 0 : keep_stack.push_back(keep);
392 : :
393 : 0 : auto val = handle_value(BasicJsonType::value_t::object, true);
394 : 0 : ref_stack.push_back(val.second);
395 : :
396 : : // check object limit
397 : 0 : if (ref_stack.back() and JSON_UNLIKELY(len != std::size_t(-1) and len > ref_stack.back()->max_size()))
398 : : {
399 : 0 : JSON_THROW(out_of_range::create(408, "excessive object size: " + std::to_string(len)));
400 : : }
401 : :
402 : 0 : return true;
403 : 0 : }
404 : :
405 : 0 : bool key(string_t& val)
406 : : {
407 : 0 : BasicJsonType k = BasicJsonType(val);
408 : :
409 : : // check callback for key
410 : 0 : const bool keep = callback(static_cast<int>(ref_stack.size()), parse_event_t::key, k);
411 : 0 : key_keep_stack.push_back(keep);
412 : :
413 : : // add discarded value at given key and store the reference for later
414 : 0 : if (keep and ref_stack.back())
415 : : {
416 : 0 : object_element = &(ref_stack.back()->m_value.object->operator[](val) = discarded);
417 : 0 : }
418 : :
419 : : return true;
420 : 0 : }
421 : :
422 : 0 : bool end_object()
423 : : {
424 : 0 : if (ref_stack.back() and not callback(static_cast<int>(ref_stack.size()) - 1, parse_event_t::object_end, *ref_stack.back()))
425 : : {
426 : : // discard object
427 : 0 : *ref_stack.back() = discarded;
428 : 0 : }
429 : :
430 : 0 : assert(not ref_stack.empty());
431 : 0 : assert(not keep_stack.empty());
432 : 0 : ref_stack.pop_back();
433 : 0 : keep_stack.pop_back();
434 : :
435 : 0 : if (not ref_stack.empty() and ref_stack.back() and ref_stack.back()->is_object())
436 : : {
437 : : // remove discarded value
438 : 0 : for (auto it = ref_stack.back()->begin(); it != ref_stack.back()->end(); ++it)
439 : : {
440 : 0 : if (it->is_discarded())
441 : : {
442 : 0 : ref_stack.back()->erase(it);
443 : 0 : break;
444 : : }
445 : 0 : }
446 : 0 : }
447 : :
448 : 0 : return true;
449 : : }
450 : :
451 : 0 : bool start_array(std::size_t len)
452 : : {
453 : 0 : const bool keep = callback(static_cast<int>(ref_stack.size()), parse_event_t::array_start, discarded);
454 : 0 : keep_stack.push_back(keep);
455 : :
456 : 0 : auto val = handle_value(BasicJsonType::value_t::array, true);
457 : 0 : ref_stack.push_back(val.second);
458 : :
459 : : // check array limit
460 : 0 : if (ref_stack.back() and JSON_UNLIKELY(len != std::size_t(-1) and len > ref_stack.back()->max_size()))
461 : : {
462 : 0 : JSON_THROW(out_of_range::create(408, "excessive array size: " + std::to_string(len)));
463 : : }
464 : :
465 : 0 : return true;
466 : 0 : }
467 : :
468 : 0 : bool end_array()
469 : : {
470 : 0 : bool keep = true;
471 : :
472 : 0 : if (ref_stack.back())
473 : : {
474 : 0 : keep = callback(static_cast<int>(ref_stack.size()) - 1, parse_event_t::array_end, *ref_stack.back());
475 : 0 : if (not keep)
476 : : {
477 : : // discard array
478 : 0 : *ref_stack.back() = discarded;
479 : 0 : }
480 : 0 : }
481 : :
482 : 0 : assert(not ref_stack.empty());
483 : 0 : assert(not keep_stack.empty());
484 : 0 : ref_stack.pop_back();
485 : 0 : keep_stack.pop_back();
486 : :
487 : : // remove discarded value
488 : 0 : if (not keep and not ref_stack.empty() and ref_stack.back()->is_array())
489 : : {
490 : 0 : ref_stack.back()->m_value.array->pop_back();
491 : 0 : }
492 : :
493 : 0 : return true;
494 : : }
495 : :
496 : 0 : bool parse_error(std::size_t /*unused*/, const std::string& /*unused*/,
497 : : const detail::exception& ex)
498 : : {
499 : 0 : errored = true;
500 : 0 : if (allow_exceptions)
501 : : {
502 : : // determine the proper exception type from the id
503 : 0 : switch ((ex.id / 100) % 100)
504 : : {
505 : : case 1:
506 : 0 : JSON_THROW(*static_cast<const detail::parse_error*>(&ex));
507 : : case 4:
508 : 0 : JSON_THROW(*static_cast<const detail::out_of_range*>(&ex));
509 : : // LCOV_EXCL_START
510 : : case 2:
511 : : JSON_THROW(*static_cast<const detail::invalid_iterator*>(&ex));
512 : : case 3:
513 : : JSON_THROW(*static_cast<const detail::type_error*>(&ex));
514 : : case 5:
515 : : JSON_THROW(*static_cast<const detail::other_error*>(&ex));
516 : : default:
517 : : assert(false);
518 : : // LCOV_EXCL_STOP
519 : : }
520 : : }
521 : 0 : return false;
522 : : }
523 : :
524 : 0 : constexpr bool is_errored() const
525 : : {
526 : 0 : return errored;
527 : : }
528 : :
529 : : private:
530 : : /*!
531 : : @param[in] v value to add to the JSON value we build during parsing
532 : : @param[in] skip_callback whether we should skip calling the callback
533 : : function; this is required after start_array() and
534 : : start_object() SAX events, because otherwise we would call the
535 : : callback function with an empty array or object, respectively.
536 : :
537 : : @invariant If the ref stack is empty, then the passed value will be the new
538 : : root.
539 : : @invariant If the ref stack contains a value, then it is an array or an
540 : : object to which we can add elements
541 : :
542 : : @return pair of boolean (whether value should be kept) and pointer (to the
543 : : passed value in the ref_stack hierarchy; nullptr if not kept)
544 : : */
545 : : template<typename Value>
546 : 0 : std::pair<bool, BasicJsonType*> handle_value(Value&& v, const bool skip_callback = false)
547 : : {
548 : 0 : assert(not keep_stack.empty());
549 : :
550 : : // do not handle this value if we know it would be added to a discarded
551 : : // container
552 : 0 : if (not keep_stack.back())
553 : : {
554 : 0 : return {false, nullptr};
555 : : }
556 : :
557 : : // create value
558 : 0 : auto value = BasicJsonType(std::forward<Value>(v));
559 : :
560 : : // check callback
561 : 0 : const bool keep = skip_callback or callback(static_cast<int>(ref_stack.size()), parse_event_t::value, value);
562 : :
563 : : // do not handle this value if we just learnt it shall be discarded
564 : 0 : if (not keep)
565 : : {
566 : 0 : return {false, nullptr};
567 : : }
568 : :
569 : 0 : if (ref_stack.empty())
570 : : {
571 : 0 : root = std::move(value);
572 : 0 : return {true, &root};
573 : : }
574 : :
575 : : // skip this value if we already decided to skip the parent
576 : : // (https://github.com/nlohmann/json/issues/971#issuecomment-413678360)
577 : 0 : if (not ref_stack.back())
578 : : {
579 : 0 : return {false, nullptr};
580 : : }
581 : :
582 : : // we now only expect arrays and objects
583 : 0 : assert(ref_stack.back()->is_array() or ref_stack.back()->is_object());
584 : :
585 : : // array
586 : 0 : if (ref_stack.back()->is_array())
587 : : {
588 : 0 : ref_stack.back()->m_value.array->push_back(std::move(value));
589 : 0 : return {true, &(ref_stack.back()->m_value.array->back())};
590 : : }
591 : :
592 : : // object
593 : 0 : assert(ref_stack.back()->is_object());
594 : : // check if we should store an element for the current key
595 : 0 : assert(not key_keep_stack.empty());
596 : 0 : const bool store_element = key_keep_stack.back();
597 : 0 : key_keep_stack.pop_back();
598 : :
599 : 0 : if (not store_element)
600 : : {
601 : 0 : return {false, nullptr};
602 : : }
603 : :
604 : 0 : assert(object_element);
605 : 0 : *object_element = std::move(value);
606 : 0 : return {true, object_element};
607 : 0 : }
608 : :
609 : : /// the parsed JSON value
610 : : BasicJsonType& root;
611 : : /// stack to model hierarchy of values
612 : 0 : std::vector<BasicJsonType*> ref_stack {};
613 : : /// stack to manage which values to keep
614 : 0 : std::vector<bool> keep_stack {};
615 : : /// stack to manage which object keys to keep
616 : 0 : std::vector<bool> key_keep_stack {};
617 : : /// helper to hold the reference for the next object element
618 : 0 : BasicJsonType* object_element = nullptr;
619 : : /// whether a syntax error occurred
620 : 0 : bool errored = false;
621 : : /// callback function
622 : : const parser_callback_t callback = nullptr;
623 : : /// whether to throw exceptions in case of errors
624 : : const bool allow_exceptions = true;
625 : : /// a discarded value for the callback
626 : 0 : BasicJsonType discarded = BasicJsonType::value_t::discarded;
627 : : };
628 : :
629 : : template<typename BasicJsonType>
630 : : class json_sax_acceptor
631 : : {
632 : : public:
633 : : using number_integer_t = typename BasicJsonType::number_integer_t;
634 : : using number_unsigned_t = typename BasicJsonType::number_unsigned_t;
635 : : using number_float_t = typename BasicJsonType::number_float_t;
636 : : using string_t = typename BasicJsonType::string_t;
637 : :
638 : : bool null()
639 : : {
640 : : return true;
641 : : }
642 : :
643 : : bool boolean(bool /*unused*/)
644 : : {
645 : : return true;
646 : : }
647 : :
648 : : bool number_integer(number_integer_t /*unused*/)
649 : : {
650 : : return true;
651 : : }
652 : :
653 : : bool number_unsigned(number_unsigned_t /*unused*/)
654 : : {
655 : : return true;
656 : : }
657 : :
658 : : bool number_float(number_float_t /*unused*/, const string_t& /*unused*/)
659 : : {
660 : : return true;
661 : : }
662 : :
663 : : bool string(string_t& /*unused*/)
664 : : {
665 : : return true;
666 : : }
667 : :
668 : : bool start_object(std::size_t /*unused*/ = std::size_t(-1))
669 : : {
670 : : return true;
671 : : }
672 : :
673 : : bool key(string_t& /*unused*/)
674 : : {
675 : : return true;
676 : : }
677 : :
678 : : bool end_object()
679 : : {
680 : : return true;
681 : : }
682 : :
683 : : bool start_array(std::size_t /*unused*/ = std::size_t(-1))
684 : : {
685 : : return true;
686 : : }
687 : :
688 : : bool end_array()
689 : : {
690 : : return true;
691 : : }
692 : :
693 : : bool parse_error(std::size_t /*unused*/, const std::string& /*unused*/, const detail::exception& /*unused*/)
694 : : {
695 : : return false;
696 : : }
697 : : };
698 : : } // namespace detail
699 : :
700 : : } // namespace nlohmann
|