From 754c38e871497013e22de894e696122cb7991d93 Mon Sep 17 00:00:00 2001 From: Aaron Burghardt Date: Fri, 27 Feb 2015 20:19:15 -0500 Subject: [PATCH 01/12] Moved buffer management into the lexer class and implemented YYFILL so that streams are read incrementally. --- src/json.hpp.re2c | 68 +++++++++++++++++++++++++++++++++++------------ 1 file changed, 51 insertions(+), 17 deletions(-) diff --git a/src/json.hpp.re2c b/src/json.hpp.re2c index 9226dc13bd..e6b8a5ab54 100644 --- a/src/json.hpp.re2c +++ b/src/json.hpp.re2c @@ -3046,11 +3046,20 @@ class basic_json using lexer_char_t = unsigned char; /// constructor with a given buffer - inline lexer(const string_t& s) noexcept - : m_content(reinterpret_cast(s.c_str())) - { + inline lexer(const string_t& s) noexcept + : m_buffer(s), m_stream(nullptr) + { + m_content = reinterpret_cast(s.c_str()); + m_start = m_cursor = m_content; + m_limit = m_content + s.size(); + } + inline lexer(std::istream* s) noexcept + : m_stream(s) + { + getline(*m_stream, m_buffer); + m_content = reinterpret_cast(m_buffer.c_str()); m_start = m_cursor = m_content; - m_limit = m_content + s.size(); + m_limit = m_content + m_buffer.size(); } /// default constructor @@ -3182,6 +3191,8 @@ class basic_json // remember the begin of the token m_start = m_cursor; +#define YYFILL(n) { size_t offset_marker = m_marker - m_start; yyfill(n); m_marker = m_start + offset_marker; } + /*!re2c re2c:define:YYCTYPE = lexer_char_t; re2c:define:YYCURSOR = m_cursor; @@ -3190,7 +3201,6 @@ class basic_json re2c:indent:string = " "; re2c:indent:top = 1; re2c:labelprefix = "basic_json_parser_"; - re2c:yyfill:enable = 0; // whitespace ws = [ \t\n\r]+; @@ -3240,8 +3250,28 @@ class basic_json // anything else is an error . { return token_type::parse_error; } */ + } + void yyfill(int n) noexcept + { + if (not m_stream or not *m_stream) return; + + ssize_t offset_start = m_start - m_content; + ssize_t offset_cursor = m_cursor - m_start; + ssize_t offset_limit = m_limit - m_start; + + m_buffer.erase(0, offset_start); + std::string line; + std::getline(*m_stream, line); + m_buffer += line; + + m_content = reinterpret_cast(m_buffer.c_str()); //reinterpret_cast(endptr) + m_start = m_content + offset_start; + m_cursor = m_start + offset_cursor; + m_limit = m_start + offset_limit; + } + /// return string representation of last read token inline string_t get_token() const noexcept { @@ -3404,14 +3434,20 @@ class basic_json } private: + /// optional input stream + std::istream* m_stream; /// the buffer + string_t m_buffer; + /// the buffer pointer const lexer_char_t* m_content = nullptr; - /// pointer to he beginning of the current symbol + /// pointer to the beginning of the current symbol const lexer_char_t* m_start = nullptr; /// pointer to the current symbol const lexer_char_t* m_cursor = nullptr; /// pointer to the end of the buffer const lexer_char_t* m_limit = nullptr; + /// YYSTATE + int m_state; }; /*! @@ -3421,24 +3457,24 @@ class basic_json { public: /// constructor for strings - inline parser(const string_t& s) : m_buffer(s), m_lexer(m_buffer) + inline parser(const string_t& s) : m_lexer(s) { // read first token get_token(); } /// a parser reading from an input stream - inline parser(std::istream& _is) + inline parser(std::istream& _is) : m_lexer(&_is) { - while (_is) - { - string_t input_line; - std::getline(_is, input_line); - m_buffer += input_line; - } +// while (_is) +// { +// string_t input_line; +// std::getline(_is, input_line); +// m_buffer += input_line; +// } // initializer lexer - m_lexer = lexer(m_buffer); +// m_lexer = std::move(lexer(_is)); // read first token get_token(); @@ -3625,8 +3661,6 @@ class basic_json } private: - /// the buffer - string_t m_buffer; /// the type of the last read token typename lexer::token_type last_token = lexer::token_type::uninitialized; /// the lexer From e4cc42cf818cb738a74c591a1e5a12dad9b107d1 Mon Sep 17 00:00:00 2001 From: Aaron Burghardt Date: Fri, 27 Feb 2015 20:27:37 -0500 Subject: [PATCH 02/12] Moved m_marker in lexer::scan() to be a member of lexer. --- src/json.hpp.re2c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/json.hpp.re2c b/src/json.hpp.re2c index e6b8a5ab54..7174c3808b 100644 --- a/src/json.hpp.re2c +++ b/src/json.hpp.re2c @@ -3186,7 +3186,7 @@ class basic_json inline token_type scan() noexcept { // pointer for backtracking information - const lexer_char_t* m_marker = nullptr; + m_marker = nullptr; // remember the begin of the token m_start = m_cursor; @@ -3258,6 +3258,7 @@ class basic_json if (not m_stream or not *m_stream) return; ssize_t offset_start = m_start - m_content; + ssize_t offset_marker = m_marker - m_start; ssize_t offset_cursor = m_cursor - m_start; ssize_t offset_limit = m_limit - m_start; @@ -3268,6 +3269,7 @@ class basic_json m_content = reinterpret_cast(m_buffer.c_str()); //reinterpret_cast(endptr) m_start = m_content + offset_start; + m_marker = m_start + offset_marker; m_cursor = m_start + offset_cursor; m_limit = m_start + offset_limit; } @@ -3442,6 +3444,8 @@ class basic_json const lexer_char_t* m_content = nullptr; /// pointer to the beginning of the current symbol const lexer_char_t* m_start = nullptr; + /// pointer for backtracking information + const lexer_char_t* m_marker = nullptr; /// pointer to the current symbol const lexer_char_t* m_cursor = nullptr; /// pointer to the end of the buffer From e3e18d7b8501965779a042de1a96f075ccfa5739 Mon Sep 17 00:00:00 2001 From: Aaron Burghardt Date: Fri, 27 Feb 2015 20:31:03 -0500 Subject: [PATCH 03/12] Deleted extraneous comment. --- src/json.hpp.re2c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/json.hpp.re2c b/src/json.hpp.re2c index 7174c3808b..777fdbbeec 100644 --- a/src/json.hpp.re2c +++ b/src/json.hpp.re2c @@ -3267,7 +3267,7 @@ class basic_json std::getline(*m_stream, line); m_buffer += line; - m_content = reinterpret_cast(m_buffer.c_str()); //reinterpret_cast(endptr) + m_content = reinterpret_cast(m_buffer.c_str()); m_start = m_content + offset_start; m_marker = m_start + offset_marker; m_cursor = m_start + offset_cursor; From 0d79e7c2a27378e149fc3e5747b3a8f22065dc20 Mon Sep 17 00:00:00 2001 From: Aaron Burghardt Date: Fri, 27 Feb 2015 20:38:05 -0500 Subject: [PATCH 04/12] Removed duplicate m_marker updates in YYFILL macro. --- src/json.hpp.re2c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/json.hpp.re2c b/src/json.hpp.re2c index 777fdbbeec..4312208096 100644 --- a/src/json.hpp.re2c +++ b/src/json.hpp.re2c @@ -3191,7 +3191,7 @@ class basic_json // remember the begin of the token m_start = m_cursor; -#define YYFILL(n) { size_t offset_marker = m_marker - m_start; yyfill(n); m_marker = m_start + offset_marker; } +#define YYFILL(n) { yyfill(n); } /*!re2c re2c:define:YYCTYPE = lexer_char_t; From b66c306d475ef36d9c51cf32830dcff3d2c00a5d Mon Sep 17 00:00:00 2001 From: Aaron Burghardt Date: Sat, 28 Feb 2015 07:13:17 -0500 Subject: [PATCH 05/12] Removed unused member m_state. --- src/json.hpp.re2c | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/json.hpp.re2c b/src/json.hpp.re2c index 4312208096..e8b2e01988 100644 --- a/src/json.hpp.re2c +++ b/src/json.hpp.re2c @@ -3450,8 +3450,6 @@ class basic_json const lexer_char_t* m_cursor = nullptr; /// pointer to the end of the buffer const lexer_char_t* m_limit = nullptr; - /// YYSTATE - int m_state; }; /*! From ec6979bf76220d8d962e58690ec2487de3c3fd11 Mon Sep 17 00:00:00 2001 From: Aaron Burghardt Date: Sat, 28 Feb 2015 08:32:12 -0500 Subject: [PATCH 06/12] Purged old commented-out code. --- src/json.hpp.re2c | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/src/json.hpp.re2c b/src/json.hpp.re2c index e8b2e01988..1a732ef3f3 100644 --- a/src/json.hpp.re2c +++ b/src/json.hpp.re2c @@ -3468,16 +3468,6 @@ class basic_json /// a parser reading from an input stream inline parser(std::istream& _is) : m_lexer(&_is) { -// while (_is) -// { -// string_t input_line; -// std::getline(_is, input_line); -// m_buffer += input_line; -// } - - // initializer lexer -// m_lexer = std::move(lexer(_is)); - // read first token get_token(); } From edb697293b016f7f169c215480eb459779887612 Mon Sep 17 00:00:00 2001 From: Aaron Burghardt Date: Sat, 28 Feb 2015 08:42:20 -0500 Subject: [PATCH 07/12] Fixed variable adjustments in yyfill(). --- src/json.hpp.re2c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/json.hpp.re2c b/src/json.hpp.re2c index 1a732ef3f3..5ed8608c4c 100644 --- a/src/json.hpp.re2c +++ b/src/json.hpp.re2c @@ -3260,7 +3260,6 @@ class basic_json ssize_t offset_start = m_start - m_content; ssize_t offset_marker = m_marker - m_start; ssize_t offset_cursor = m_cursor - m_start; - ssize_t offset_limit = m_limit - m_start; m_buffer.erase(0, offset_start); std::string line; @@ -3268,10 +3267,10 @@ class basic_json m_buffer += line; m_content = reinterpret_cast(m_buffer.c_str()); - m_start = m_content + offset_start; + m_start = m_content; m_marker = m_start + offset_marker; m_cursor = m_start + offset_cursor; - m_limit = m_start + offset_limit; + m_limit = m_start + m_buffer.size() - 1; } /// return string representation of last read token From 268fd444e65e57149d2399e4d978d0102feb6dfe Mon Sep 17 00:00:00 2001 From: Aaron Burghardt Date: Sat, 28 Feb 2015 22:14:57 -0500 Subject: [PATCH 08/12] Added comments to new method yyfill. --- src/json.hpp.re2c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/json.hpp.re2c b/src/json.hpp.re2c index 5ed8608c4c..7aad968029 100644 --- a/src/json.hpp.re2c +++ b/src/json.hpp.re2c @@ -3253,6 +3253,7 @@ class basic_json } + /// append data from the stream to the internal buffer void yyfill(int n) noexcept { if (not m_stream or not *m_stream) return; @@ -3261,6 +3262,9 @@ class basic_json ssize_t offset_marker = m_marker - m_start; ssize_t offset_cursor = m_cursor - m_start; + // The parser generator expects a minimum of n bytes to be appended, + // but by appending a line of data we will never split a token, so + // it should be safe to ignore the parameter. m_buffer.erase(0, offset_start); std::string line; std::getline(*m_stream, line); From 2855c70c27cff13701fd300a60ee6d5d38950b3f Mon Sep 17 00:00:00 2001 From: Aaron Burghardt Date: Sat, 28 Feb 2015 22:36:57 -0500 Subject: [PATCH 09/12] Use inplace configuration for yyfill and disable the parameter to yyfill. --- src/json.hpp.re2c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/json.hpp.re2c b/src/json.hpp.re2c index 7aad968029..b328880b5f 100644 --- a/src/json.hpp.re2c +++ b/src/json.hpp.re2c @@ -3191,13 +3191,13 @@ class basic_json // remember the begin of the token m_start = m_cursor; -#define YYFILL(n) { yyfill(n); } - /*!re2c re2c:define:YYCTYPE = lexer_char_t; re2c:define:YYCURSOR = m_cursor; re2c:define:YYLIMIT = m_limit; re2c:define:YYMARKER = m_marker; + re2c:define:YYFILL = "{ yyfill(); }"; + re2c:yyfill:parameter = 0; re2c:indent:string = " "; re2c:indent:top = 1; re2c:labelprefix = "basic_json_parser_"; @@ -3254,7 +3254,7 @@ class basic_json } /// append data from the stream to the internal buffer - void yyfill(int n) noexcept + void yyfill() noexcept { if (not m_stream or not *m_stream) return; @@ -3262,9 +3262,6 @@ class basic_json ssize_t offset_marker = m_marker - m_start; ssize_t offset_cursor = m_cursor - m_start; - // The parser generator expects a minimum of n bytes to be appended, - // but by appending a line of data we will never split a token, so - // it should be safe to ignore the parameter. m_buffer.erase(0, offset_start); std::string line; std::getline(*m_stream, line); From 87746280cafe739107376861f3feafc600289281 Mon Sep 17 00:00:00 2001 From: Aaron Burghardt Date: Sun, 1 Mar 2015 06:21:47 -0500 Subject: [PATCH 10/12] Added parse() for streams. --- src/json.hpp.re2c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/json.hpp.re2c b/src/json.hpp.re2c index b328880b5f..7f7c098c12 100644 --- a/src/json.hpp.re2c +++ b/src/json.hpp.re2c @@ -1716,6 +1716,12 @@ class basic_json return parser(s).parse(); } + /// deserialize from stream + static basic_json parse(std::istream& i) + { + return parser(i).parse(); + } + /// deserialize from stream friend std::istream& operator>>(std::istream& i, basic_json& j) { From 396f64a0585a5dfc16a384a4845d64f672b950ec Mon Sep 17 00:00:00 2001 From: Aaron Burghardt Date: Mon, 2 Mar 2015 15:25:09 -0500 Subject: [PATCH 11/12] Replaced leading tabs with spaces (4 per tab). --- src/json.hpp.re2c | 54 +++++++++++++++++++++++------------------------ 1 file changed, 27 insertions(+), 27 deletions(-) diff --git a/src/json.hpp.re2c b/src/json.hpp.re2c index 7f7c098c12..ea39159ae8 100644 --- a/src/json.hpp.re2c +++ b/src/json.hpp.re2c @@ -3052,18 +3052,18 @@ class basic_json using lexer_char_t = unsigned char; /// constructor with a given buffer - inline lexer(const string_t& s) noexcept - : m_buffer(s), m_stream(nullptr) - { - m_content = reinterpret_cast(s.c_str()); - m_start = m_cursor = m_content; - m_limit = m_content + s.size(); - } - inline lexer(std::istream* s) noexcept + inline lexer(const string_t& s) noexcept + : m_buffer(s), m_stream(nullptr) + { + m_content = reinterpret_cast(s.c_str()); + m_start = m_cursor = m_content; + m_limit = m_content + s.size(); + } + inline lexer(std::istream* s) noexcept : m_stream(s) { - getline(*m_stream, m_buffer); - m_content = reinterpret_cast(m_buffer.c_str()); + getline(*m_stream, m_buffer); + m_content = reinterpret_cast(m_buffer.c_str()); m_start = m_cursor = m_content; m_limit = m_content + m_buffer.size(); } @@ -3259,26 +3259,26 @@ class basic_json } - /// append data from the stream to the internal buffer - void yyfill() noexcept - { - if (not m_stream or not *m_stream) return; + /// append data from the stream to the internal buffer + void yyfill() noexcept + { + if (not m_stream or not *m_stream) return; - ssize_t offset_start = m_start - m_content; - ssize_t offset_marker = m_marker - m_start; - ssize_t offset_cursor = m_cursor - m_start; + ssize_t offset_start = m_start - m_content; + ssize_t offset_marker = m_marker - m_start; + ssize_t offset_cursor = m_cursor - m_start; - m_buffer.erase(0, offset_start); - std::string line; - std::getline(*m_stream, line); - m_buffer += line; + m_buffer.erase(0, offset_start); + std::string line; + std::getline(*m_stream, line); + m_buffer += line; - m_content = reinterpret_cast(m_buffer.c_str()); - m_start = m_content; - m_marker = m_start + offset_marker; - m_cursor = m_start + offset_cursor; - m_limit = m_start + m_buffer.size() - 1; - } + m_content = reinterpret_cast(m_buffer.c_str()); + m_start = m_content; + m_marker = m_start + offset_marker; + m_cursor = m_start + offset_cursor; + m_limit = m_start + m_buffer.size() - 1; + } /// return string representation of last read token inline string_t get_token() const noexcept From b52dc7922f9af7cb485167df8bcce6d25046271b Mon Sep 17 00:00:00 2001 From: Aaron Burghardt Date: Mon, 2 Mar 2015 15:16:38 -0500 Subject: [PATCH 12/12] Added parser callback to enable processing parsed data as it is processed, including the ability to reject individual elements. --- src/json.hpp.re2c | 149 +++++++++++++++++++++++++++++++++++++++------- 1 file changed, 128 insertions(+), 21 deletions(-) diff --git a/src/json.hpp.re2c b/src/json.hpp.re2c index ea39159ae8..1f8b633fe1 100644 --- a/src/json.hpp.re2c +++ b/src/json.hpp.re2c @@ -230,9 +230,35 @@ class basic_json string, ///< string value boolean, ///< boolean value number_integer, ///< number value (integer) - number_float ///< number value (floating-point) + number_float, ///< number value (floating-point) + discarded ///< (internal) indicates the parser callback chose not to keep the value }; + ////////////////////////// + // JSON parser callback // + ////////////////////////// + + /// JSON callback event enumeration + enum class parse_event_t : uint8_t + { + object_start, ///< start an object scope (found a '{' token) + object_end, ///< end of an object scope (found '}' token) + array_start, ///< start of an array scope (found '[' token) + array_end, ///< end of an array scope (found ']' token) + key, ///< found an object key within an object scope + value ///< a value in an appropriate context (i.e., following a tag in an object scope) + }; + + /// per-element parser callback type + using parser_callback_t = std::function& parsed)>; + + /// default parser callback returns true to keep all elements + static bool default_callback(int, parse_event_t, const nlohmann::basic_json&) + { + return true; + } + /*! @brief comparison operator for JSON value types @@ -331,6 +357,7 @@ class basic_json switch (m_type) { case (value_t::null): + case (value_t::discarded): { break; } @@ -596,6 +623,7 @@ class basic_json switch (m_type) { case (value_t::null): + case (value_t::discarded): { break; } @@ -787,6 +815,12 @@ class basic_json return m_type == value_t::string; } + // return whether value is discarded + inline bool is_discarded() const noexcept + { + return m_type == value_t::discarded; + } + /// return the type of the object (implicit) inline operator value_t() const noexcept { @@ -1310,6 +1344,7 @@ class basic_json switch (m_type) { case (value_t::null): + case (value_t::discarded): { break; } @@ -1572,6 +1607,11 @@ class basic_json } break; } + case (value_t::discarded): + { + return false; + break; + } } return false; @@ -1655,6 +1695,11 @@ class basic_json } break; } + case (value_t::discarded): + { + return false; + break; + } } // We only reach this line if we cannot compare values. In that case, @@ -1711,15 +1756,15 @@ class basic_json ///////////////////// /// deserialize from string - static basic_json parse(const string_t& s) + static basic_json parse(const string_t& s, parser_callback_t cb = default_callback) { - return parser(s).parse(); + return parser(s, cb).parse(); } /// deserialize from stream - static basic_json parse(std::istream& i) + static basic_json parse(std::istream& i, parser_callback_t cb = default_callback) { - return parser(i).parse(); + return parser(i, cb).parse(); } /// deserialize from stream @@ -1772,6 +1817,11 @@ class basic_json return "boolean"; } + case (value_t::discarded): + { + return "discarded"; + } + default: { return "number"; @@ -1993,6 +2043,10 @@ class basic_json return std::to_string(m_value.number_float); } + case (value_t::discarded): + { + return ""; + } default: { return "null"; @@ -3465,14 +3519,14 @@ class basic_json { public: /// constructor for strings - inline parser(const string_t& s) : m_lexer(s) + inline parser(const string_t& s, parser_callback_t cb = default_callback) : callback(cb), m_lexer(s) { // read first token get_token(); } /// a parser reading from an input stream - inline parser(std::istream& _is) : m_lexer(&_is) + inline parser(std::istream& _is, parser_callback_t cb = default_callback) : callback(cb), m_lexer(&_is) { // read first token get_token(); @@ -3481,7 +3535,7 @@ class basic_json /// public parser interface inline basic_json parse() { - basic_json result = parse_internal(); + basic_json result = parse_internal(true); expect(lexer::token_type::end_of_input); @@ -3490,14 +3544,19 @@ class basic_json private: /// the actual parser - inline basic_json parse_internal() + inline basic_json parse_internal(bool keep) { + auto result = basic_json(value_t::discarded); + switch (last_token) { case (lexer::token_type::begin_object): { - // explicitly set result to object to cope with {} - basic_json result(value_t::object); + if (keep and (keep = callback(depth++, parse_event_t::object_start, result))) + { + // explicitly set result to object to cope with {} + result = basic_json(value_t::object); + } // read next token get_token(); @@ -3506,6 +3565,10 @@ class basic_json if (last_token == lexer::token_type::end_object) { get_token(); + if (keep and not (keep = callback(--depth, parse_event_t::object_end, result))) + { + result = basic_json(value_t::discarded); + } return result; } @@ -3522,27 +3585,44 @@ class basic_json expect(lexer::token_type::value_string); const auto key = m_lexer.get_string(); + bool keep_tag = false; + if (keep) + { + keep_tag = callback(depth, parse_event_t::key, basic_json(key)); + } + // parse separator (:) get_token(); expect(lexer::token_type::name_separator); // parse value get_token(); - result[key] = parse_internal(); + auto value = parse_internal(keep); + if (keep and keep_tag and not value.is_discarded()) + { + result[key] = value; + } } while (last_token == lexer::token_type::value_separator); // closing } expect(lexer::token_type::end_object); get_token(); + if (keep and not callback(--depth, parse_event_t::object_end, result)) + { + result = basic_json(value_t::discarded); + } return result; } case (lexer::token_type::begin_array): { - // explicitly set result to object to cope with [] - basic_json result(value_t::array); + if (keep and (keep = callback(depth++, parse_event_t::array_start, result))) + { + // explicitly set result to object to cope with [] + result = basic_json(value_t::array); + } // read next token get_token(); @@ -3551,6 +3631,10 @@ class basic_json if (last_token == lexer::token_type::end_array) { get_token(); + if (not callback(--depth, parse_event_t::array_end, result)) + { + result = basic_json(value_t::discarded); + } return result; } @@ -3564,13 +3648,21 @@ class basic_json } // parse value - result.push_back(parse_internal()); + auto value = parse_internal(keep); + if (keep and not value.is_discarded()) + { + result.push_back(value); + } } while (last_token == lexer::token_type::value_separator); // closing ] expect(lexer::token_type::end_array); get_token(); + if (keep and not callback(--depth, parse_event_t::array_end, result)) + { + result = basic_json(value_t::discarded); + } return result; } @@ -3578,26 +3670,30 @@ class basic_json case (lexer::token_type::literal_null): { get_token(); - return basic_json(nullptr); + result = basic_json(nullptr); + break; } case (lexer::token_type::value_string): { const auto s = m_lexer.get_string(); get_token(); - return basic_json(s); + result = basic_json(s); + break; } case (lexer::token_type::literal_true): { get_token(); - return basic_json(true); + result = basic_json(true); + break; } case (lexer::token_type::literal_false): { get_token(); - return basic_json(false); + result = basic_json(false); + break; } case (lexer::token_type::value_number): @@ -3619,13 +3715,14 @@ class basic_json if (float_val == int_val) { // we basic_json not lose precision -> return int - return basic_json(int_val); + result = basic_json(int_val); } else { // we would lose precision -> returnfloat - return basic_json(float_val); + result = basic_json(float_val); } + break; } default: @@ -3637,6 +3734,12 @@ class basic_json throw std::invalid_argument(error_msg); } } + + if (keep and not callback(depth, parse_event_t::value, result)) + { + result = basic_json(value_t::discarded); + } + return result; } /// get next token from lexer @@ -3659,6 +3762,10 @@ class basic_json } private: + /// levels of recursion + int depth = 0; + /// callback function + parser_callback_t callback; /// the type of the last read token typename lexer::token_type last_token = lexer::token_type::uninitialized; /// the lexer