Feature Request: JSONPointer (RFC 6901) Support
- Dominant language
- C++
- Stars
- 5
- Forks
- 4
- PR merge metrics
- No merged PRs in 30d
Description
# Feature Request: JSONPointer (RFC 6901) Support
## Summary
Add JSONPointer (RFC 6901) path-based access to JSRL, enabling standard, ergonomic navigation of nested JSON structures using a string-based path syntax.
## Motivation
### Current Limitation
Today, accessing nested JSON data in JSRL requires verbose method chaining:
```cpp
// Current approach - verbose and error-prone
Json config = load_config();
std::string host = config["server"]["database"]["primary"]["host"].as_string();
int timeout = config["settings"]["connection"]["timeout"].as_number_sint();
```
**Problems with this approach:**
1. **Verbose**: Every level requires a separate `[]` operation
2. **Type-unsafe**: Each intermediate access can throw if the wrong type
3. **Hard to parameterize**: Cannot easily pass paths as strings
4. **No standard**: Custom navigation logic needed for dynamic paths
### The Solution: JSONPointer
JSONPointer (RFC 6901) is a well-established standard for describing locations in JSON documents using simple path strings:
```cpp
// With JSONPointer - clean and standard
Json config = load_config();
std::string host = config.at_pointer("/server/database/primary/host").as_string();
int timeout = config.at_pointer("/settings/connection/timeout").as_number_sint();
```
## Benefits
### 1. **Improved Developer Experience**
**Before:**
```cpp
// Accessing deeply nested data
try {
auto& user = doc["users"][0]["profile"]["settings"];
std::string theme = user["theme"].as_string();
} catch (Json::KeyError& e) {
// Which key failed? Hard to tell.
}
```
**After:**
```cpp
// Clear, single-line access with precise error messages
std::string theme = doc.at_pointer("/users/0/profile/settings/theme").as_string();
// Error: "Object key 'theme' not found at position 4"
```
### 2. **Dynamic Path Construction**
JSONPointer enables runtime path construction for flexible data access:
```cpp
// Configuration-driven field access
std::vector required_fields = {
"/user/name",
"/user/email",
"/user/profile/avatar"
};
for (const auto& path : required_fields) {
if (doc.get_pointer(path).is_null()) {
log_error("Missing required field: " + path);
}
}
```
### 3. **Standardization & Interoperability**
JSONPointer is an IETF standard (RFC 6901) used across:
- JSON Schema validation
- JSON Patch operations (RFC 6902)
- Modern JSON libraries (nlohmann::json, simdjson, RapidJSON)
- REST APIs and configuration systems
Adopting this standard improves JSRL's interoperability with existing tools and systems.
### 4. **Simplified Error Handling**
The `get_pointer()` method provides graceful degradation with default values:
```cpp
// Before: complex try-catch logic
int timeout = 30; // default
try {
if (config.has_key("settings")) {
auto& settings = config["settings"];
if (settings.has_key("timeout")) {
timeout = settings["timeout"].as_number_sint();
}
}
} catch (...) {
// use default
}
// After: single line with clear intent
int timeout = config.get_pointer("/settings/timeout", 30).as_number_sint();
```
### 5. **Better Testability**
JSONPointer paths are easily defined in test data:
```cpp
// Test data can specify expected paths
struct TestCase {
std::string path;
std::string expected_value;
};
std::vector tests = {
{"/version", "1.0.0"},
{"/api/endpoints/0/path", "/users"},
{"/api/endpoints/0/method", "GET"}
};
for (const auto& tc : tests) {
EXPECT_EQ(tc.expected_value,
response.at_pointer(tc.path).as_string());
}
```
## Use Cases
### 1. **Configuration Management**
Adobe Analytics and data processing pipelines often use deeply nested configuration:
```cpp
// Loading configuration with safe defaults
Json config = load_analytics_config();
auto db_host = config.get_pointer("/database/primary/host", "localhost");
auto db_port = config.get_pointer("/database/primary/port", 5432);
auto max_connections = config.get_pointer("/database/pool/max_connections", 100);
auto query_timeout = config.get_pointer("/query/timeout_ms", 30000);
```
### 2. **API Response Processing**
Processing structured API responses:
```cpp
Json response = fetch_user_analytics();
// Extract multiple metrics with clear paths
auto page_views = response.at_pointer("/metrics/pageviews/total").as_number_sint();
auto unique_visitors = response.at_pointer("/metrics/visitors/unique").as_number_sint();
auto bounce_rate = response.at_pointer("/metrics/engagement/bounce_rate").as_number_float();
```
### 3. **Data Validation**
Validating required fields in incoming data:
```cpp
bool validate_user_data(Json const& user) {
// List of required paths
static const std::vector required = {
"/user_id",
"/email",
"/profile/created_at",
"/profile/subscription/tier"
};
for (const auto& path : required) {
if (user.get_pointer(path).is_null()) {
log_validation_error("Missing field: " + path);
return false;
}
}
return true;
}
```
### 4. **Transformation Pipelines**
Data transformation with path-based field mapping:
```cpp
// Map source paths to destination paths
std::map field_mapping = {
{"/legacy_user_id", "/user/id"},
{"/user_name", "/user/profile/display_name"},
{"/created", "/metadata/created_at"}
};
Json transform(Json const& source) {
Json::ObjectBody result;
for (const auto& [src, dst] : field_mapping) {
auto value = source.get_pointer(src);
if (not value.is_null()) {
result.emplace_back(dst, value);
}
}
return Json(result);
}
```
Contributor guide
Assessment
This issue has not been assessed yet.