JSON utilities for Vix.cpp applications.
The JSON module provides a small, practical layer on top of nlohmann::json. It is designed to keep common JSON work simple while staying fully compatible with the underlying nlohmann::json API.
It provides:
- JSON construction helpers,
- JSON parsing helpers,
- JSON serialization helpers,
- safe access and conversion helpers,
- a small JPath syntax for dynamic nested access,
- a lightweight internal Simple value model,
- examples,
- tests,
- benchmarks.
Use this module when you need to:
- build JSON API responses,
- parse JSON text,
- read and write JSON files,
- safely access user-provided JSON data,
- mutate nested JSON values with dynamic paths,
- create small internal structured payloads without exposing
nlohmann::jsoneverywhere.
The module is header-only and exposes the public target:
vix::jsonFor most users, include:
#include <vix/json.hpp>or:
#include <vix/json/json.hpp>Then use:
using namespace vix::json;#include <vix/json.hpp>
#include <iostream>
int main()
{
using namespace vix::json;
Json user = o(
"id", 1,
"name", "Ada",
"skills", a("C++", "JSON", "Vix.cpp"));
jset(user, "profile.city", "Kampala");
if (const Json *name = jget(user, "name"))
std::cout << "name: " << name->get<std::string>() << "\n";
std::cout << dumps_pretty(user) << "\n";
return 0;
}include/vix/json/
build.hpp
convert.hpp
dumps.hpp
jpath.hpp
json.hpp
loads.hpp
Simple.hppThe module exposes two main JSON aliases:
using Json = nlohmann::json;
using OrderedJson = nlohmann::ordered_json;Json is the default type.
OrderedJson is used by the object builder o() to preserve insertion order and produce deterministic output.
Header:
#include <vix/json/build.hpp>auto user = o(
"id", 42,
"name", "Gaspard",
"active", true);o() returns OrderedJson.
The number of arguments must be even:
auto ok = o("name", "Vix.cpp", "version", "1.0.0");This is invalid and should not compile:
auto bad = o("name", "Vix.cpp", "missing_value");Json values = a(1, 2, 3, "hello", true);Json config = kv({
{"name", Json("Vix.cpp")},
{"debug", Json(true)},
{"port", Json(8080)},
});kv() is useful when pairs are created dynamically. For normal static code, prefer o().
Header:
#include <vix/json/loads.hpp>Json j = loads(R"({"id": 1, "name": "Ada"})");loads() throws if the input is invalid.
auto maybe = try_loads("{bad json");
if (!maybe)
{
// invalid JSON
}try_loads() never throws. It returns std::nullopt on failure.
Json config = load_file("config.json");load_file() throws if:
- the file cannot be opened,
- the file is empty,
- the content is invalid JSON.
Safe version:
auto config = try_load_file("config.json");
if (!config)
{
// missing file, empty file, or invalid JSON
}Header:
#include <vix/json/dumps.hpp>std::string text = dumps(j);
std::string pretty = dumps_pretty(j);Default indentation is 2 spaces.
Custom indentation:
std::string text = dumps(j, 4);std::string text = dumps_compact(j);std::string text = dumps_compact(j, true);When ensure_ascii is true, non-ASCII characters are escaped.
dump_file("out/config.json", j);dump_file() writes through a temporary file and then replaces the target file. It also creates parent directories when needed.
This reduces the chance of leaving a partially written JSON file.
Header:
#include <vix/json/convert.hpp>const Json *name = ptr(j, "name");
if (name)
{
// key exists
}Array access:
const Json *first = ptr(arr, 0);ptr() returns nullptr when the value is missing or the shape is invalid.
auto id = get_opt<int>(j, "id");
if (id)
{
// valid integer
}int port = get_or<int>(j, "port", 8080);int id = ensure<int>(j, "id");ensure() throws when the value is missing or has the wrong type.
Recommended usage:
External input: get_opt() or get_or()
Trusted internal data: ensure()
Low-level access: ptr()Header:
#include <vix/json/jpath.hpp>JPath is a small path syntax for navigating and mutating JSON values.
It supports:
user.name
settings.theme
users[0].email
roles[1]
["complex.key"].value
["a b c"][0]It is not a full JSONPath implementation. It is intentionally small and focused on common application use cases.
Json j = loads(R"({
"user": {
"name": "Ada",
"roles": ["admin", "editor"]
}
})");
if (const Json *role = jget(j, "user.roles[1]"))
{
std::cout << role->get<std::string>() << "\n";
}The const version:
const Json *jget(const Json &, path)does not throw for invalid or missing paths. It returns nullptr.
Json j = Json::object();
jset(j, "user.profile.name", "Gaspard");
jset(j, "user.roles[0]", "admin");jset() creates missing intermediate objects and arrays.
Result:
{
"user": {
"profile": {
"name": "Gaspard"
},
"roles": ["admin"]
}
}Json j = Json::object();
Json *name = jget(j, "user.name");
*name = "Ada";The mutable version can create missing nodes and may throw on invalid syntax.
Header:
#include <vix/json/Simple.hpp>Simple.hpp provides a lightweight JSON-like model used for internal Vix APIs.
It does not depend on nlohmann::json.
It supports:
- null,
- bool,
- int64,
- double,
- string,
- array,
- object.
Main types:
token
array_t
kvstoken a = nullptr;
token b = true;
token c = 42;
token d = 3.14;
token e = "hello";array_t values = simple_array({
1,
"two",
true,
nullptr,
});Mutation:
values.push_int(42);
values.push_string("Vix.cpp");
values.ensure(10) = "created";kvs user = simple_obj({
"name", "Ada",
"age", 30,
"active", true,
});Typed access:
std::string name = user.get_string_or("name", "unknown");
std::int64_t age = user.get_i64_or("age", 0);
bool active = user.get_bool_or("active", false);Mutation:
user.set_string("city", "Kampala");
user.set_bool("verified", true);kvs root;
kvs &user = root.ensure_object("user");
user.set_string("name", "Ada");
array_t &skills = user.ensure_array("skills");
skills.push_string("C++");
skills.push_string("JSON");Header:
#include <vix/json/convert.hpp>kvs user = simple_obj({
"name", "Ada",
"age", 30,
});
Json j = simple_to_json(user);Aliases:
Json j1 = to_json(token("hello"));
Json j2 = to_json(simple_array({1, 2, 3}));
Json j3 = to_json(simple_obj({"name", "Ada"}));Examples are located in:
examples/Main examples:
quick_start.cpp
builders.cpp
jpath.cpp
io.cppSimple examples:
examples/json_simple/
01_basic_values.cpp
02_arrays.cpp
03_objects.cpp
04_nested.cpp
05_mutation.cpp
06_iteration.cpp
07_merge_and_erase.cpp
08_to_nlohmann_json.cpp
bench_simple_vs_nlohmann.cppBuild examples:
vix build --build-target all -v -- -DVIX_JSON_BUILD_EXAMPLES=ONRun examples:
./build-ninja/bin/vix_json_quick
./build-ninja/bin/vix_json_builders
./build-ninja/bin/vix_json_jpath
./build-ninja/bin/vix_json_io
./build-ninja/bin/vix_json_simple_01_basic_values
./build-ninja/bin/vix_json_simple_08_to_nlohmann_jsonTests are located in:
tests/Current test files:
json_build_test.cpp
json_convert_test.cpp
json_dumps_test.cpp
json_include_test.cpp
json_jpath_test.cpp
json_loads_test.cpp
json_simple_test.cppBuild and run tests:
vix build --build-target all -v -- -DVIX_JSON_BUILD_TESTS=ON
vix testsOr build everything:
vix build --build-target all -v -- \
-DVIX_JSON_BUILD_EXAMPLES=ON \
-DVIX_JSON_BUILD_TESTS=ON \
-DVIX_JSON_BUILD_BENCHMARKS=ON
vix testsBenchmarks are located in:
benchmarks/Benchmark files:
json_build_bench.cpp
json_convert_bench.cpp
json_dumps_bench.cpp
json_jpath_bench.cpp
json_loads_bench.cpp
json_simple_bench.cppBuild benchmarks:
vix build --build-target all -v -- -DVIX_JSON_BUILD_BENCHMARKS=ONRun benchmarks:
./build-ninja/bin/json_build_bench
./build-ninja/bin/json_convert_bench
./build-ninja/bin/json_dumps_bench
./build-ninja/bin/json_jpath_bench
./build-ninja/bin/json_loads_bench
./build-ninja/bin/json_simple_benchBenchmark result snapshots are stored in:
benchmarks/results/Current benchmark notes:
benchmarks/README.md
benchmarks/results/json_build_bench.md
benchmarks/results/json_convert_bench.md
benchmarks/results/json_dumps_bench.md
benchmarks/results/json_jpath_bench.md
benchmarks/results/json_loads_bench.md
benchmarks/results/json_simple_bench.md
benchmarks/results/vix_json_simple_bench_simple_vs_nlohmann.md| Option | Default | Description |
|---|---|---|
VIX_JSON_BUILD_EXAMPLES |
OFF |
Build JSON examples |
VIX_JSON_BUILD_TESTS |
OFF |
Build JSON tests |
VIX_JSON_BUILD_BENCHMARKS |
OFF |
Build JSON benchmarks |
Example:
vix build --build-target all -v -- \
-DVIX_JSON_BUILD_EXAMPLES=ON \
-DVIX_JSON_BUILD_TESTS=ON \
-DVIX_JSON_BUILD_BENCHMARKS=ONThe module uses nlohmann_json.
Provider resolution:
- use installed
nlohmann_jsonCMake config package, - use CMake find-module mode,
- fetch a header-only fallback for local builds.
Public target:
vix::jsonThe module itself is header-only.
Standalone installation installs the headers and exports the package target.
Typical standalone install flow:
vix build --build-target all -vFor packaging, the CMake install rules export:
vix_jsonTargetsWhen built as part of the Vix umbrella, it exports into:
VixTargetsUse:
o("key", value)
a(value1, value2)
kv({{"key", Json(value)}})Use:
loads(text)
try_loads(text)
load_file(path)
try_load_file(path)Use:
dumps(j)
dumps_pretty(j)
dumps_compact(j)
dump_file(path, j)Use:
ptr(j, key)
ptr(arr, index)
get_opt<T>(j)
get_or<T>(j, fallback)
ensure<T>(j)Use:
jget(j, "user.name")
jset(j, "user.name", "Ada")Use:
token
array_t
kvs
simple_array(...)
simple_obj(...)The module keeps nlohmann::json compatibility instead of hiding it behind a new large abstraction.
The helpers are intentionally small:
build.hppimproves construction readability,loads.hppmakes parsing explicit,dumps.hppmakes serialization and file output explicit,convert.hppreduces repetitive safe-access boilerplate,jpath.hpphandles dynamic nested paths,Simple.hppprovides a minimal internal payload model.
The goal is not to replace nlohmann::json. The goal is to make JSON work easier and more consistent inside Vix.cpp applications.
- JSON documentation: https://docs.vixcpp.com/modules/json/
- JSON API reference: https://docs.vixcpp.com/modules/json/api-reference
- Build command: https://docs.vixcpp.com/cli/build
- Tests command: https://docs.vixcpp.com/cli/tests
- Documentation: https://docs.vixcpp.com/
- Engineering notes: https://blog.vixcpp.com/
- Registry: https://registry.vixcpp.com/
- GitHub: https://github.com/vixcpp/vix