JSON Decoding with ard/decode
The ard/decode module provides a composable system for decoding JSON and other data into Ard values with detailed error reporting.
The decode module provides:
- Primitive decoders for strings, integers, floats, and booleans
- Composable decoders that can be combined for complex types
- Detailed error messages with path information for debugging
- JSON parsing with the
from_jsonfunction
use ard/decodeuse ard/io
fn main() { let json_str = "{\"name\": \"Alice\", \"age\": 30}" let data = decode::from_json(json_str).expect("Invalid JSON")
let name = decode::run(data, decode::field("name", decode::string)).expect("Failed to decode") let age = decode::run(data, decode::field("age", decode::int)).expect("Failed to decode")
io::print("Name: {name}, Age: {age.to_str()}")}Basic Decoders
Section titled “Basic Decoders”fn string(data: Dynamic) Str![Error]
Section titled “fn string(data: Dynamic) Str![Error]”Decode a string value from Dynamic data.
use ard/decode
let data = Dynamic::from("hello")let str = decode::run(data, decode::string).expect("Failed to decode")fn int(data: Dynamic) Int![Error]
Section titled “fn int(data: Dynamic) Int![Error]”Decode an integer value from Dynamic data.
use ard/decode
let data = Dynamic::from(42)let num = decode::run(data, decode::int).expect("Failed to decode")fn float(data: Dynamic) Float![Error]
Section titled “fn float(data: Dynamic) Float![Error]”Decode a float value from Dynamic data.
use ard/decode
let data = Dynamic::from(3.14)let num = decode::run(data, decode::float).expect("Failed to decode")fn bool(data: Dynamic) Bool![Error]
Section titled “fn bool(data: Dynamic) Bool![Error]”Decode a boolean value from Dynamic data.
use ard/decode
let data = Dynamic::from(true)let flag = decode::run(data, decode::bool).expect("Failed to decode")Composite Decoders
Section titled “Composite Decoders”fn nullable(decoder: Decoder<$T>) fn(Dynamic) $T?![Error]
Section titled “fn nullable(decoder: Decoder<$T>) fn(Dynamic) $T?![Error]”Create a decoder that handles nullable values. Returns none for null data, or the result of applying the inner decoder.
use ard/decode
let string_decoder = decode::nullable(decode::string)let data = Dynamic::from_void()let maybe_str = decode::run(data, string_decoder).expect("") // nonefn list(decoder: Decoder<$T>) fn(Dynamic) [$T]![Error]
Section titled “fn list(decoder: Decoder<$T>) fn(Dynamic) [$T]![Error]”Create a decoder for a list of items. Applies the inner decoder to each element and reports detailed errors with array indices.
use ard/decode
let data = decode::from_json("[1, 2, 3]").expect("Invalid JSON")let numbers = decode::run(data, decode::list(decode::int)).expect("Failed to decode")fn field(name: Str, with: Decoder<$T>) Decoder<$T>
Section titled “fn field(name: Str, with: Decoder<$T>) Decoder<$T>”Create a decoder for a specific field in an object. Combines the decoder for the field value with path tracking.
use ard/decode
let data = decode::from_json("{\"name\": \"Alice\"}").expect("Invalid JSON")let name = decode::run(data, decode::field("name", decode::string)).expect("Failed to decode")fn path(subpath: [PathSegment], with: Decoder<$T>) Decoder<$T>
Section titled “fn path(subpath: [PathSegment], with: Decoder<$T>) Decoder<$T>”Create a decoder for a nested path supporting both field names and array indices. PathSegment is a union type (Str | Int) allowing you to navigate through objects and arrays.
use ard/decode
// Navigate through nested objectslet data = decode::from_json("{\"user\": {\"profile\": {\"age\": 30}}}").expect("Invalid JSON")let age = decode::run(data, decode::path(["user", "profile", "age"], decode::int)).expect("Failed to decode")
// Navigate through arrays and objectslet data2 = decode::from_json("{\"users\": [{\"name\": \"Alice\"}, {\"name\": \"Bob\"}]}").expect("Invalid JSON")let name = decode::run(data2, decode::path(["users", 0, "name"], decode::string)).expect("Failed to decode")fn map(key: Decoder<$Key>, value: Decoder<$Value>) Decoder<[$Key:$Value]>
Section titled “fn map(key: Decoder<$Key>, value: Decoder<$Value>) Decoder<[$Key:$Value]>”Create a decoder for maps/objects with custom key and value decoders.
use ard/decode
let data = decode::from_json("{\"alice\": 30, \"bob\": 25}").expect("Invalid JSON")let ages = decode::run(data, decode::map(decode::string, decode::int)).expect("Failed to decode")fn one_of(first: Decoder<$T>, others: [Decoder<$T>]) Decoder<$T>
Section titled “fn one_of(first: Decoder<$T>, others: [Decoder<$T>]) Decoder<$T>”Create a decoder that tries multiple decoders in sequence, returning the first successful result.
use ard/decode
let data = Dynamic::from(42)let flexible_decoder = decode::one_of(decode::string, [decode::int, decode::float])let result = decode::run(data, flexible_decoder).expect("Failed to decode")fn dynamic(data: Dynamic) Dynamic![Error]
Section titled “fn dynamic(data: Dynamic) Dynamic![Error]”Decoder that simply maintains the Dynamic value as-is. This can be used as a no-op or simple identity decoder.
use ard/decode
let data = Dynamic::from("anything")let result = decode::run(data, decode::dynamic).expect("Failed to decode")JSON and Utility Functions
Section titled “JSON and Utility Functions”fn from_json(json: Str) Dynamic!Str
Section titled “fn from_json(json: Str) Dynamic!Str”Parse a JSON string into a Dynamic value. Returns an error if the JSON is invalid.
use ard/decode
let data = decode::from_json("{\"name\": \"Alice\"}").expect("Invalid JSON")fn is_void(data: Dynamic) Bool
Section titled “fn is_void(data: Dynamic) Bool”Check if a Dynamic value is null/void.
use ard/decode
let data = Dynamic::from_void()if decode::is_void(data) { // handle null}fn run(data: Dynamic, decoder: Decoder<$T>) $T![Error]
Section titled “fn run(data: Dynamic, decoder: Decoder<$T>) $T![Error]”Apply a decoder to Dynamic data. This is a shorthand for applying a decoder function directly.
use ard/decode
let data = Dynamic::from(42)let num = decode::run(data, decode::int).expect("Failed to decode")Error Handling
Section titled “Error Handling”struct Error
Section titled “struct Error”Represents a decoding error with detailed context.
expected: Str- What type was expectedfound: Str- What was actually foundpath: [Str]- The path to the error location (e.g., [“user”, “age”])
The Error struct implements ToString for readable error messages:
use ard/decode
let data = Dynamic::from("not_a_number")match decode::run(data, decode::int) { err(errors) => { for err in errors { io::print(err.to_str()) // "Decode error: expected Int, found "not_a_number"" } }, ok(_) => {}}Examples
Section titled “Examples”Decode a Simple Object
Section titled “Decode a Simple Object”use ard/decodeuse ard/io
fn main() { let json = "{\"name\": \"Alice\", \"age\": 30}" let data = decode::from_json(json).expect("Invalid JSON")
let name = decode::run(data, decode::field("name", decode::string)).expect("Failed") let age = decode::run(data, decode::field("age", decode::int)).expect("Failed")
io::print("Name: {name}") io::print("Age: {age.to_str()}")}Decode a List
Section titled “Decode a List”use ard/decodeuse ard/io
fn main() { let json = "[1, 2, 3, 4, 5]" let data = decode::from_json(json).expect("Invalid JSON")
let numbers = decode::run(data, decode::list(decode::int)).expect("Failed to decode")
for num in numbers { io::print(num.to_str()) }}Decode with Error Details
Section titled “Decode with Error Details”use ard/decodeuse ard/io
fn main() { let json = "{\"user\": {\"name\": \"Alice\", \"age\": \"thirty\"}}" let data = decode::from_json(json).expect("Invalid JSON")
let decoder = decode::field("user", decode::field("age", decode::int) )
match decode::run(data, decoder) { ok(age) => io::print(age.to_str()), err(errors) => { for error in errors { io::print(error.to_str()) // "Decode error: expected Int, found "thirty" at user.age" } } }}Decode Nullable Fields
Section titled “Decode Nullable Fields”use ard/decodeuse ard/iouse ard/maybe
struct Person { name: Str, email: Str?}
fn main() { let json1 = "{\"name\": \"Alice\", \"email\": \"alice@example.com\"}" let json2 = "{\"name\": \"Bob\", \"email\": null}"
let decoder = decode::field("email", decode::nullable(decode::string))
let data1 = decode::from_json(json1).expect("") let email1 = decode::run(data1, decoder).expect("") // some("alice@example.com")
let data2 = decode::from_json(json2).expect("") let email2 = decode::run(data2, decoder).expect("") // none}Flexible Decoding with one_of
Section titled “Flexible Decoding with one_of”use ard/decodeuse ard/io
fn main() { // Decoder that accepts either a string or an integer let flexible = decode::one_of(decode::string, [decode::int])
let str_data = Dynamic::from("hello") let int_data = Dynamic::from(42)
let str_result = decode::run(str_data, flexible).expect("") let int_result = decode::run(int_data, flexible).expect("")}