Boost C++ Libraries Home Libraries People FAQ More

PrevUpHomeNext

Push Parser

In this tutorial we are going to use the incremental json::reader parser to build another kind of incremental parser, so we are going to introduce a distinction between incremental pull parsers and incremental push parsers. The main difference between them is the direction of control. With pull parsers, like json::reader, the user extracts or pulls one token after another, whereas with push parser the tokens are automatically pushed to the user via callback functions.

We will use json::reader to build the push parser, because pull parsers are well-suited to create other kinds of parser interfaces. The serialization output archives that we saw in a previous tutorial is another example of a higher-level parser build on top of pull parsers. This tutorial demonstrates how json::reader can be used to create a push parser.

A push parser iterates over the JSON input and invokes callback functions for each parsed data item. Each data type has a distinct callback function. The user provides the implemention of these callback functions. The design is a variation of the Builder pattern, and this is how XML SAX parsers work.

Definitions

First we define the push_parser class which takes the callback functions as a template parameter.[5]

#include <trial/protocol/json/reader.hpp>

template <typename Callbacks>
class push_parser
{
public:
    push_parser(const json::reader& reader) : reader(reader) {}

    void parse();

private:
    Callbacks callbacks;
    json::reader reader;
};

The Callbacks template parameter must be a class that implements a member function for each callback function. The Callbacks class looks something like this:

#include <cstdint>
#include <string>

class my_callbacks
{
public:
    void on_null();
    void on_boolean(bool);
    void on_integer(std::intmax_t);
    void on_number(double);
    void on_string(const std::string&);
    void on_begin_array();
    void on_end_array();
    void on_begin_object();
    void on_end_object();
};

We are not going to implement my_callbacks here, although a simple implementation could be to simply print the type and value in each callback function.

Execution

After these preliminary definitions, we have now arrived at the crux of the problem: how to implement the push_parser::parse() function. Fortunately that is very simple using a pull parser:

  1. Iterate over the JSON input using json::reader::next().
    1. Identify the current token with json::reader::symbol().
    2. Invoke the appropriate callback function. The current value for data tokens is obtained with json::reader::value<T>().

Here is the entire implementation in its full glory:

void push_parser::parse()
{
    do
    {
        switch (reader.symbol())
        {
        case json::symbol::null:
            callbacks.on_null();
            break;

        case json::symbol::boolean:
            callbacks.on_boolean(reader.value<bool>());
            break;

        case json::symbol::integer:
            callbacks.on_integer(reader.value<std::intmax_t>());
            break;

        case json::symbol::number:
            callbacks.on_number(reader.value<double>());
            break;

        case json::symbol::string:
            callbacks.on_string(reader.value<std::string>());
            break;

        case json::symbol::begin_array:
            callbacks.on_begin_array();
            break;

        case json::symbol::end_array:
            callbacks.on_end_array();
            break;

        case json::symbol::begin_object:
            callbacks.on_begin_object();
            break;

        case json::symbol::end_object:
            callbacks.on_end_object();
            break;

        default:
            break;
        }

    } while (reader.next());
}

Finally, we use the above push parser as follows:

json::reader reader("[null,true,42]"); // Replace with actual JSON input
push_parser<my_callbacks> parser(reader);
parser.parse();



[5] We could also have used a polymorphic interface for the callback functions.


PrevUpHomeNext