Skip to content

Commit

Permalink
Added parser callback to enable processing parsed data as it is proce…
Browse files Browse the repository at this point in the history
…ssed, including the ability to reject individual elements.
  • Loading branch information
aburgh committed Mar 2, 2015
1 parent 396f64a commit b52dc79
Showing 1 changed file with 128 additions and 21 deletions.
149 changes: 128 additions & 21 deletions src/json.hpp.re2c
Original file line number Diff line number Diff line change
Expand Up @@ -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<bool(int depth, parse_event_t event,
const nlohmann::basic_json<ObjectType, ArrayType, StringType, BooleanType, NumberIntegerType, NumberFloatType, Allocator>& parsed)>;

/// default parser callback returns true to keep all elements
static bool default_callback(int, parse_event_t, const nlohmann::basic_json<ObjectType, ArrayType, StringType, BooleanType, NumberIntegerType, NumberFloatType, Allocator>&)
{
return true;
}

/*!
@brief comparison operator for JSON value types

Expand Down Expand Up @@ -331,6 +357,7 @@ class basic_json
switch (m_type)
{
case (value_t::null):
case (value_t::discarded):
{
break;
}
Expand Down Expand Up @@ -596,6 +623,7 @@ class basic_json
switch (m_type)
{
case (value_t::null):
case (value_t::discarded):
{
break;
}
Expand Down Expand Up @@ -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
{
Expand Down Expand Up @@ -1310,6 +1344,7 @@ class basic_json
switch (m_type)
{
case (value_t::null):
case (value_t::discarded):
{
break;
}
Expand Down Expand Up @@ -1572,6 +1607,11 @@ class basic_json
}
break;
}
case (value_t::discarded):
{
return false;
break;
}
}

return false;
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -1772,6 +1817,11 @@ class basic_json
return "boolean";
}

case (value_t::discarded):
{
return "discarded";
}

default:
{
return "number";
Expand Down Expand Up @@ -1993,6 +2043,10 @@ class basic_json
return std::to_string(m_value.number_float);
}

case (value_t::discarded):
{
return "<discarded>";
}
default:
{
return "null";
Expand Down Expand Up @@ -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();
Expand All @@ -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);

Expand All @@ -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();
Expand All @@ -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;
}

Expand All @@ -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();
Expand All @@ -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;
}

Expand All @@ -3564,40 +3648,52 @@ 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;
}

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):
Expand All @@ -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:
Expand All @@ -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
Expand All @@ -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
Expand Down

0 comments on commit b52dc79

Please sign in to comment.