- C++ 98.7%
- CMake 1.3%
|
|
||
|---|---|---|
| LibJSON | ||
| Tests | ||
| .gitignore | ||
| CMakeLists.txt | ||
| LICENSE | ||
| README.md | ||
LibJSON
A basic two-way JSON implementation for C++. Mostly compatible with RFC 8259.
Usage
The preferred way of using this library is pulling it in via CMake's FetchContent.
include(FetchContent)
FetchContent_Declare(
libjson
GIT_REPOSITORY https://code.3011.io/TennesseeTrash/LibJSON.git
GIT_TAG v0.0.2
)
FetchContent_MakeAvailable(
libjson
)
If you'd rather not use CMake, the path is a bit more difficult, but it shouldn't be too
problematic. The library is written such that you just need to add the TUs in the Source
directory to your build and add Include to your include paths. Note that this may change in
the future.
Make sure to use a relatively recent toolchain, the library depends on some C++23 features. Clang 19 is known to work.
Documentation
For encoding your data to JSON, include <JSON/Printer.hpp>, and prepare a std::string.
Encoding is as simple as creating an instance of a Printer on top of a string. Most standard
containers should work out of the box, but keep in mind the library intentionally supports only
double as a numeric type.
#include <JSON/Printer.hpp>
int main()
{
std::string buffer;
JSON::Printer printer(buffer);
printer.Print("Hello!");
}
The printer can be extended to consume custom types via ADL-provided PrintHook functions.
#include <JSON/Printer.hpp>
struct Person
{
std::string Name;
double Height;
std::vector<std::string> Hobbies;
};
void PrintHook(JSON::Printer &printer, const Person &person)
{
printer.PrintObject(
"name", person.Name,
"height", person.Height,
"hobbies", person.Hobbies
);
}
Once the PrintHook is provided, the type can be printed identically to the default
supported types.
std::string buffer;
JSON::Printer printer(buffer);
Person john {
.Name = "John Doe",
.Height = 1.91,
.Hobbies = { "Fishing", "Skiing", "Diving" },
};
printer.Print(john);
After Print is called, the buffer is filled with the appropriate value. The printer itself keeps
minimal state, so it's possible to call Print multiple times on the same buffer. This can be
useful when serializing something like JSONL, but keep in mind this produces malformed
JSON by default.
Parsing is different from printing by default. The parser (implemented in <JSON/Parser.hpp>)
uses custom parsing types to handle objects and arrays. This requires manually calling the decoder
API in a pull-parser fashion to extract the data from raw JSON. This allows the library to stay
allocation free (with caveats), while handling all the supported JSON types. However, for basic
tasks this API is cumbersome, so the parser provides a unified Parse<T>() function, which
behaves very similarly to its Print counterpart. By default, Parse<T>() cannot handle standard
containers. To enable their support, include <JSON/ParserHooks.hpp>. This header includes support
for std::string, std::vector, std::unordered_map, and std::tuple. Support for custom types
can be added via an ADL-provided ParseHook, similar to its printer variant. To make writing the
parser a bit more pleasant (including parsing objects with shuffled map items), the library provides
a StructHelper.
The Parser does not check that it's at the end of the buffer once it's done parsing the current
item. This done intentionally to enable symmetrical handling of JSONL and similar without changing
the parser. To check whether the parser has consumed the whole input explicitly,
use Done() after extracting the value.
#include <JSON/Parser.hpp>
#include <JSON/ParserHooks.hpp>
#include <JSON/Utility.hpp>
void ParseHook(JSON::Parser &parser, Person &person)
{
constexpr JSON::StructHelper helper {
JSON::StructMember { "name", &Person::Name },
JSON::StructMember { "height", &Person::Height },
JSON::StructMember { "hobbies", &Person::Hobbies },
};
helper.Parse(parser.AsValue(), person);
}
With this hook, the Person struct can now be parsed. Note that the parsed type must be default
initializable. Similar to the printer, the parser can be used for parsing sequences of JSON values
by calling the Parse<T>() function several times.
std::string buffer; // Assume we have filled this buffer with an encoded Person.
JSON::Parser parser(buffer);
auto person = parser.Parse<Person>();
Strings
When printing, the library escapes unsafe values (mostly control characters), but it does not escape UTF-8, that passes through the library without changes.
When parsing, the String parser helper lets you choose to either get the raw string value, with
out unescaping by calling .Raw(). This returns a std::string_view, so it costs no allocations,
or extra scans. Alternatively, the type also exposes an .Unescaped() member function, that
unescapes everything into UTF-8, but returns a std::string. This costs an allocation, and an extra
pass over the buffer. Note, however that it does not validate UTF-8.