Overview
structr
is a high-performance R package designed for strict JSON parsing and serialization with schema validation. Whether you’re working with APIs, databases, or complex nested JSON, structr
ensures that your data conforms exactly to the required structure—both when reading and writing.
Powered by Rust (serde
, simd_json
) and exposed to R via {extendr}
, it provides significant speed advantages over traditional R JSON tools while offering more precise control over data structure and types.
Features
-
Schema Definition in R: Use expressive R functions like
s_map
,s_vector
,s_integer
,s_double
,s_string
,s_logical
,s_optional
, ands_date
. - Strict Validation: Catch type mismatches, missing fields, unexpected fields, and NULLs with clear error messages.
- High Performance: Combines validation with parsing for efficient data ingestion.
- Bidirectional Support: Parse JSON and serialize R data back to JSON, validating against the same schema.
Planned Features
- Support for enums and more complex/custom validations.
- Better date/time parsing and formatting options.
- Clear, location-aware error messages help identify problems early.
Installation
Prerequisites
You must have Rust (rustc
, cargo
) installed. You can install via rustup:
Make sure Rust >= 1.70.
Install from GitHub
# Install 'remotes' if needed
install.packages("remotes")
remotes::install_github("ixpantia/structr")
Quick Start
Define a Schema
library(structr)
schema <- s_map(
x = s_integer(),
y = s_double(),
z = s_optional(s_string())
)
compiled <- build_structure(schema)
Parse JSON
json <- '{"x": 1, "y": 99.9, "z": "value"}'
parse_json(json, compiled)
#> $x
#> [1] 1
#>
#> $y
#> [1] 99.9
#>
#> $z
#> [1] "value"
Serialize JSON
r_data <- list(x = 1L, y = 99.9, z = "value")
serialize_json(r_data, compiled, pretty = TRUE)
#> {
#> "y": 99.9,
#> "z": "value",
#> "x": 1
#> }
Error Handling
Invalid input raises clear, structured errors:
bad_data <- '{"x": "not-an-int", "y": 10.5, "z": "ok"}'
try(parse_json(bad_data, compiled))
#> Error in parse_json(bad_data, compiled) : ExpectedSigned at character 0
Handling Optional and Missing Fields
All fields are required by default. Use s_optional()
to allow null or missing values.
parse_json('{"x": 1, "y": 99.9}', compiled) # OK
#> $x
#> [1] 1
#>
#> $y
#> [1] 99.9
try(parse_json('{"x": null, "y": 99.9}', compiled)) # Error
#> Error in parse_json("{\"x\": null, \"y\": 99.9}", compiled) :
#> ExpectedSigned at character 0
Advanced Example: Nested and Complex Structures
order_schema <- s_map(
order_id = s_string(),
order_date = s_date(format = "%Y-%m-%d"),
customer = s_map(
customer_id = s_integer(),
name = s_string(),
email = s_optional(s_string())
),
items = s_vector(s_map(
sku = s_string(),
product_name = s_string(),
quantity = s_integer(),
unit_price = s_double()
)),
shipping_address = s_map(
street = s_string(),
city = s_string(),
zip_code = s_string(),
country = s_string()
),
notes = s_optional(s_string())
)
compiled_order <- build_structure(order_schema)
# Parse or serialize safely
order_json <- '{"order_id":"ORD-1","order_date":"2024-01-01","customer":{"customer_id":1,"name":"Alice"},"items":[],"shipping_address":{"street":"1 Main","city":"City","zip_code":"00000","country":"USA"}}'
parsed <- parse_json(order_json, compiled_order)
serialize_json(parsed, compiled_order, pretty = TRUE)
#> {
#> "customer": {
#> "email": null,
#> "customer_id": 1,
#> "name": "Alice"
#> },
#> "items": [],
#> "notes": null,
#> "order_id": "ORD-1",
#> "shipping_address": {
#> "zip_code": "00000",
#> "city": "City",
#> "country": "USA",
#> "street": "1 Main"
#> },
#> "order_date": "2024-01-01"
#> }
Strict Serialization Example
log_schema <- s_map(
eventId = s_string(),
eventDate = s_date("%Y%m%d"),
eventType = s_string(),
userId = s_integer(),
details = s_map(
source = s_string(),
metadata = s_optional(s_map(
key = s_optional(s_string()),
value = s_optional(s_double())
))
)
)
compiled_log <- build_structure(log_schema)
r_log <- list(
eventId = "evt-001",
eventDate = as.Date("2024-08-01"),
eventType = "login",
userId = 123L,
details = list(
source = "web",
metadata = list(key = "duration", value = 320.5)
)
)
serialize_json(r_log, compiled_log, pretty = TRUE)
#> {
#> "eventDate": "20240801",
#> "eventType": "login",
#> "userId": 123,
#> "eventId": "evt-001",
#> "details": {
#> "metadata": {
#> "value": 320.5,
#> "key": "duration"
#> },
#> "source": "web"
#> }
#> }
License
MIT License. See LICENSE.
Contributing
Please file issues or submit PRs at https://github.com/ixpantia/structr.
Author
Andres Quintero andres@ixpantia.com