Jopp is another JSON library for C++. Jopp
-
Is header-only to simplify integration. The parser can read from different input ranges.
-
Supports non-blocking I/O, by using an API similar to
std::form_chars/std::to_chars -
Does not complain when there is more data to be processed after the first JSON object/array has ended.
-
Parses/serializes numbers with
std::from_chars/std::to_chars. This means that inf and nan are supported. Please notice that the behaviour with regards to inf and nan may depend on compiler options such as-ffinite-math. -
Always maps a JSON
numberto the type double. This differs from the choice made by jansson and nlohmann, which may parse numbers as integers. This choice has been made since there is no standard way of inferring that a number written as an integer should actually be a double. Also, this choice prevents information loss, when the data should be pared by other implementations. -
Has an optional limit on the tree depth to control memory usage. By default, it is set to 1024 levels.
-
Objects uses
std::mapas backing store. This means that- Jopp is immune against hash attacks.
- Keys are always sorted according to the C local.
The following program demonstrates how to read data from stdin, and write it back to stdout. For details about different features, see the corresponding file:
| Feature | Include file |
|---|---|
| Delimiters and escape char handling | lib/delimiters.hpp |
| Misc | lib/utils.hpp |
| Parser | lib/parser.hpp |
| Serializer | lib/serializer.hpp |
| Data storage | lib/types.hpp |
#include <jopp/parser.hpp>
#include <jopp/serializer.hpp>
#include <unistd.h>
#include <errno.h>
int main()
{
jopp::container root;
{
// Read data from stdin
jopp::parser parser{root};
std::array<char, 4096> buffer{};
auto old_ec = jopp::parser_error_code::completed;
while(true)
{
auto const bytes_read = ::read(STDIN_FILENO, std::data(buffer), std::size(buffer));
if(bytes_read == 0)
{
if(old_ec != jopp::parser_error_code::completed)
{
fprintf(stderr, "error: %s\n", to_string(old_ec));
return -1;
}
break;
}
if(bytes_read == -1 && errno == EAGAIN)
{
// In a real scenario, control should be moved to a different procedure, probably
// selected by select(2) et.al.
continue;
}
auto const res = parser.parse(std::span{std::data(buffer), static_cast<size_t>(bytes_read)});
if(res.ec == jopp::parser_error_code::completed)
{ break; }
else
if(res.ec != jopp::parser_error_code::more_data_needed)
{
fprintf(stderr, "error: %s\n", to_string(res.ec));
// Error condition: invalid data
return - 1;
}
old_ec = res.ec;
}
}
{
// Write data to stdout
jopp::serializer serializer{root};
std::array<char, 4096> buffer{};
while(true)
{
auto const res = serializer.serialize(buffer);
auto write_ptr = std::begin(buffer);
auto bytes_to_write = res.ptr - std::begin(buffer);
while(bytes_to_write != 0)
{
auto const bytes_written = ::write(STDOUT_FILENO, write_ptr, bytes_to_write);
if(bytes_written == 0)
{ return -1; }
if(bytes_written == -1 && errno == EAGAIN)
{
// In a real scenario, control should be moved to a different procedure,
// probably selected by select(2) et.al.
continue;
}
write_ptr += bytes_written;
bytes_to_write -= bytes_written;
}
if(res.ec == jopp::serializer_error_code::completed)
{
// Success
return 0;
}
if(res.ec != jopp::serializer_error_code::buffer_is_full)
{
// Error condition: invalid data
return -1;
}
}
}
}