doc/compile_time_accessors.md
Note: This feature requires C++26 Static Reflection support (P2996) and is currently only available with experimental compilers. You must enable it with -DSIMDJSON_STATIC_REFLECTION=ON when building.
simdjson provides compile-time JSONPath and JSON Pointer accessors that validate paths against struct definitions at compile time and generate optimized accessor code with zero runtime overhead. This combines the safety of compile-time type checking with the performance of pre-parsed, pre-validated access paths.
-std=c++26 -freflection -fexpansion-statements-DSIMDJSON_STATIC_REFLECTION=ONCompile Time:
Runtime:
When you provide a struct type, the compiler validates the entire path at compile time:
struct User {
std::string name;
int age;
std::vector<std::string> emails;
};
// R"( ... )" is a C++ raw string literal.
const padded_string json = R"({
"name": "Alice",
"age": 30,
"emails": ["[email protected]", "[email protected]"]
})"_padded;
ondemand::parser parser;
auto doc = parser.iterate(json);
// Compile-time validation: checks that User has "name" field of type std::string
std::string name;
auto result = ondemand::json_path::at_path_compiled<User, ".name">(doc);
result.get(name); // name = "Alice"
// Compile-time validation: checks that "emails" is array-like with string elements
std::string email;
result = ondemand::json_path::at_path_compiled<User, ".emails[0]">(doc);
result.get(email); // email = "[email protected]"
Benefits:
What gets validated:
When you omit the struct type, the path is parsed at compile time but not validated:
const padded_string json = R"({
"name": "Alice",
"age": 30,
"address": {"city": "Boston"}
})"_padded;
ondemand::parser parser;
auto doc = parser.iterate(json);
// No compile-time validation - path is only parsed
std::string name;
auto result = ondemand::json_path::at_path_compiled<".name">(doc);
result.get(name); // name = "Alice"
std::string_view city;
result = ondemand::json_path::at_path_compiled<".address.city">(doc);
result.get(city); // city = "Boston"
Benefits:
Use when:
JSONPath uses dot notation and bracket notation for field access:
| Syntax | Description | Example |
|---|---|---|
.field | Dot notation for field access | .name, .address.city |
["field"] | Bracket notation with quotes | ["name"], ["address"]["city"] |
[index] | Array index access | [0], [1] |
| Mixed | Combination of notations | .emails[0], ["users"][0].name |
$ prefix | Optional root indicator | $.name, $["name"] |
struct Address {
std::string city;
int zip;
};
struct Person {
std::string name;
int age;
Address address;
std::vector<std::string> emails;
};
// Dot notation
at_path_compiled<Person, ".name">(doc)
at_path_compiled<Person, ".address.city">(doc)
// Bracket notation
at_path_compiled<Person, "[\"name\"]">(doc)
at_path_compiled<Person, "[\"address\"][\"city\"]">(doc)
// Array access
at_path_compiled<Person, ".emails[0]">(doc)
at_path_compiled<Person, ".emails[1]">(doc)
// Mixed notation
at_path_compiled<Person, ".address[\"zip\"]">(doc)
at_path_compiled<Person, "[\"emails\"][0]">(doc)
// With root indicator
at_path_compiled<Person, "$.name">(doc)
at_path_compiled<Person, "$.address.city">(doc)
JSON Pointer (RFC 6901) uses slash-separated paths:
| Syntax | Description | Example |
|---|---|---|
/field | Field access | /name, /address/city |
/index | Array index | /0, /1 |
~0 | Escaped ~ | /field~0name → field~name |
~1 | Escaped / | /field~1name → field/name |
struct Car {
std::string make;
std::string model;
int64_t year;
std::vector<double> tire_pressure;
};
// Field access
at_pointer_compiled<Car, "/make">(doc)
at_pointer_compiled<Car, "/model">(doc)
// Array access
at_pointer_compiled<Car, "/tire_pressure/0">(doc)
at_pointer_compiled<Car, "/tire_pressure/1">(doc)
// Root pointer (returns whole document)
at_pointer_compiled<Car, "">(doc)
at_pointer_compiled<Car, "/">(doc)
// With type validation
template<typename T, constevalutil::fixed_string Path, typename DocOrValue>
simdjson_result<value> at_path_compiled(DocOrValue& doc_or_val);
// Without validation
template<constevalutil::fixed_string Path, typename DocOrValue>
simdjson_result<value> at_path_compiled(DocOrValue& doc_or_val);
// With type validation
template<typename T, constevalutil::fixed_string Pointer, typename DocOrValue>
simdjson_result<value> at_pointer_compiled(DocOrValue& doc_or_val);
// Without validation
template<constevalutil::fixed_string Pointer, typename DocOrValue>
simdjson_result<value> at_pointer_compiled(DocOrValue& doc_or_val);
Extract values directly into variables with compile-time type checking:
// JSONPath
template<typename T, constevalutil::fixed_string Path>
struct path_accessor {
template<typename DocOrValue, typename FieldType>
static error_code extract_field(DocOrValue& doc_or_val, FieldType& target);
};
// JSON Pointer
template<typename T, constevalutil::fixed_string Pointer>
struct pointer_accessor {
template<typename DocOrValue, typename FieldType>
static error_code extract_field(DocOrValue& doc_or_val, FieldType& target);
};
Example:
struct User {
std::string name;
int age;
};
ondemand::parser parser;
auto doc = parser.iterate(json);
// Extract directly into variable
std::string name;
ondemand::json_path::path_accessor<User, ".name">::extract_field(doc, name);
int age;
ondemand::json_path::pointer_accessor<User, "/age">::extract_field(doc, age);
The compiler verifies that the target variable type matches the field type at the path.
#include "simdjson.h"
using namespace simdjson;
struct Car {
std::string make;
std::string model;
int64_t year;
std::vector<double> tire_pressure;
};
int main() {
const padded_string json = R"({
"make": "Toyota",
"model": "Camry",
"year": 2018,
"tire_pressure": [40.1, 39.9, 37.7, 40.4]
})"_padded;
ondemand::parser parser;
auto doc = parser.iterate(json);
// Type-validated access
std::string make;
auto result = ondemand::json_path::at_path_compiled<Car, ".make">(doc);
result.get(make); // make = "Toyota"
// Array access with validation
double pressure;
result = ondemand::json_path::at_path_compiled<Car, ".tire_pressure[1]">(doc);
result.get(pressure); // pressure = 39.9
return 0;
}
#include "simdjson.h"
using namespace simdjson;
int main() {
const padded_string json = R"({
"user": {
"name": "Alice",
"preferences": {
"theme": "dark",
"notifications": true
}
}
})"_padded;
ondemand::parser parser;
auto doc = parser.iterate(json);
// No validation - works with any JSON structure
std::string_view theme;
auto result = ondemand::json_path::at_path_compiled<".user.preferences.theme">(doc);
result.get(theme); // theme = "dark"
bool notifications;
result = ondemand::json_path::at_path_compiled<".user.preferences.notifications">(doc);
result.get(notifications); // notifications = true
return 0;
}
#include "simdjson.h"
using namespace simdjson;
struct Person {
std::string name;
int age;
std::vector<std::string> emails;
};
int main() {
const padded_string json = R"({
"name": "Bob",
"age": 25,
"emails": ["[email protected]", "[email protected]"]
})"_padded;
ondemand::parser parser;
auto doc = parser.iterate(json);
// Extract with type validation
std::string name;
ondemand::json_path::path_accessor<Person, ".name">::extract_field(doc, name);
// name = "Bob"
int age;
ondemand::json_path::pointer_accessor<Person, "/age">::extract_field(doc, age);
// age = 25
std::string email;
ondemand::json_path::path_accessor<Person, ".emails[0]">::extract_field(doc, email);
// email = "[email protected]"
return 0;
}
Compile-time errors occur when:
static_assert failurestatic_assert failurestatic_assert failureRuntime errors occur when:
struct User {
std::string name;
int age;
};
// Compile-time error: no "email" field in User
// auto result = ondemand::json_path::at_path_compiled<User, ".email">(doc);
// Compile-time error: age is not an array
// auto result = ondemand::json_path::at_path_compiled<User, ".age[0]">(doc);
// Runtime error if JSON doesn't have "name" field
auto result = ondemand::json_path::at_path_compiled<User, ".name">(doc);
std::string name;
if (result.get(name) != SUCCESS) {
// Handle error
}
Compile-time accessors provide:
Compared to runtime at_path() and at_pointer():
Use compile-time accessors when:
Use runtime at_path()/at_pointer() when: