Boost C++ Libraries Home Libraries People FAQ More

PrevUpHomeNext

Incremental Processing

Serialization and document processing is build on top of incremental processing. Incremental processing can also be used directly for more efficient processing such as searching for keys in a JSON file without having to convert strings or from JSON into C++ types.

We are going to start with incremental generation to create JSON formatted output using the json::writer. Afterwards we will use the json::reader to parse JSON formatted input.

Generating fundamental types

The json::writer is used to incrementally generate a JSON formatted buffer. We can either write a fundamental type, or use tags to write special tokens.

We first need to include a couple of headers.

#include <trial/protocol/buffer/string.hpp>
#include <trial/protocol/json/writer.hpp>

Let us generate a boolean value:

std::string output;
json::writer writer(output);

writer.write(true);

assert(output == "true");

We can also generate a null value, which means that the current entry does not have a value. Think of it as an uninitialized optional value. We pass a tag as template parameter to indicate that null should be inserted.

std::string output;
json::writer writer(output);

writer.write<json::token::null>();

assert(output == "null");

Generating array

Containers have to start with with a begin bracket and terminate with an end bracket. These brackets must be written explicitly with a tag. The separators between entries are automatically inserted.

#include <trial/protocol/buffer/string.hpp>
#include <trial/protocol/json/writer.hpp>

std::string output;
json::writer writer(output);

writer.write<json::token::begin_array>();
assert(output == "[");

writer.write(true);
assert(output == "[true");

writer.write(2);
assert(output == "[true,2");

writer.write(3.0);
assert(output == "[true,2,3.0");

writer.write("alpha");
assert(output == "[true,2,3.0,\"alpha\"");

writer.write<json::token::end_array>();
assert(output == "[true,2,3.0.\"alpha\"]");

Counting keys

We now turn our attention to incremental parsing. json::reader is a pull parser that lazily parses a single token in the input. The json::reader::next() function is used to advance the cursor to the next token.

Suppose we have string called haystack containing a JSON object, and that we want to count all key-value pairs with a given key called needle.

For the sake of simplicity we are going to assume that there are no nested containers.

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

std::size_t prefix_count(const std::string& haystack,
                         const std::string& needle)
{
  std::size_t count = 0;
  json::reader reader(haystack);
  do
  {
    auto key = reader.value<std::string>();
    if (key == needle)
    {
      ++count;
    }
    reader.next(); // Skip key
  } while (reader.next()); // Skip value
  return count;
}

In the above example we convert the current key from JSON to std::string before doing the comparison.

We can optimize this by converting the needle into a JSON string and then comparing it with the unconverted JSON string. We will use the json::writer for that.

#include <trial/protocol/buffer/string.hpp>
#include <trial/protocol/json/reader.hpp>
#include <trial/protocol/json/writer.hpp>

std::size_t prefix_count_fast(const std::string& haystack,
                              const std::string& needle)
{
  std::string literal;
  json::writer writer(literal);
  writer.value(needle);

  std::size_t count = 0;
  json::reader reader(haystack);
  do
  {
    const auto& key = reader.literal();
    if (key == literal)
    {
      ++count;
    }
    reader.next(); // Skip key
  } while (reader.next()) // Skip value
  return count;
}


PrevUpHomeNext