Home | Libraries | People | FAQ | More |
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.
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");
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\"]");
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; }