Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Usage Guide

A set of utilities that enhance miniserde to provide more of the serde functionality — without the bloat.

Why midiserde?

miniserde gives you JSON serialization with a tiny dependency footprint and no proc-macro-heavy codegen. But it stops short of the convenience many projects need: derive macros, field attributes, value conversion, and common type adapters.

serde gives you everything — but at a cost. Its trait-based design and extensive monomorphization can blow up binary size and compile times, especially when you have hundreds or thousands of serializable types.

midiserde fills the gap: miniserde’s lean core, plus the ergonomics you expect.

Use cases

  • Embedded and WebAssembly — Strip serde monomorphization bloat, slash executable size, and cut compile times. Miniserde’s simpler trait design generates far less monomorphized code than serde’s.
  • Large API bindings — SDKs and clients with thousands of data types can reduce bloat by many megabytes by switching to miniserde + midiserde instead of serde.
  • Lightweight services — Fast builds and smaller binaries without sacrificing attribute-based customization.

Installation

[dependencies]
midiserde = "0.1"

The macros feature is enabled by default, providing Deserialize and Serialize derive macros.

For runnable examples, run cargo run -p midiserde_examples --example <name> with: rename, defaults, flatten, skip, skip_serializing_if, custom_with, base64, chrono, two_stage.

Derive macros

Use #[derive(Deserialize)] and #[derive(Serialize)] with the #[mini(...)] attribute namespace:

#![allow(unused)]
fn main() {
use midiserde::{json, Deserialize, Serialize};

#[derive(Deserialize, Serialize)]
struct Config {
    name: String,
    port: u16,
}

let config = Config { name: "api".into(), port: 8080 };
let j = json::to_string(&config);
let parsed: Config = json::from_str(&j)?;
}

Customization options

All attributes use the #[mini(...)] namespace. See midiserde_examples for full examples of each.

#[mini(rename = "json_key")] — Map a different JSON key to this field (or enum variant). Useful for camelCase APIs (displayName) or API naming conventions.

#[mini(default)] — When the field is missing during deserialization, use Default::default(). Requires T: Default.

#[mini(default = "path::to::func")] — When the field is missing, call a custom function fn() -> T instead of Default::default(). The function path must be a valid Rust path (e.g. default_timeout_ms or my_module::defaults::retries). No T: Default bound is required.

#[mini(with = "module::path")] — Custom serialization/deserialization. The module must provide begin(out: &mut Option<T>) -> &mut dyn Visitor and serialize(value: &T) -> Fragment. See the built-in with modules or custom_with example.

#[mini(flatten)] — Two modes:

  1. Struct flatten: Inline a nested struct’s fields into the parent JSON object (e.g. pagination metadata at the top level). Multiple struct flattens are supported with no ordering constraints.
  2. Map flatten: Use HashMap<String, Value> or BTreeMap<String, Value> to capture unknown JSON keys (at most one per struct).

Key routing is automatic: each struct knows its own fields via FlattenMap::accepts_key, so no field lists are needed — even when mixing multiple struct flattens with a map flatten. All flatten serialization is zero-copy streaming via SerializeMapBuilder (no intermediate Value): struct flattens use the derived impl, and HashMap/BTreeMap flattens use midiserde’s built-in impls. See midiserde_examples -> flatten for the full pattern.

#[mini(skip)] — Skip both serialization and deserialization. The field always uses Default::default() on deserialize.

#[mini(skip_serializing)] — Omit the field when serializing; still deserialize it if present in JSON.

#[mini(skip_deserializing)] — Never read from JSON; always Default::default(). The field is still serialized.

#[mini(skip_serializing_if = "path")] — Call a fn(&T) -> bool; omit the field when serializing if it returns true. Common: Option::is_none, Vec::is_empty, or a custom predicate.

#![allow(unused)]
fn main() {
// Quick reference
#[derive(Deserialize, Serialize)]
struct ApiResponse {
    #[mini(rename = "displayName")]
    display_name: String,
    #[mini(default)]
    count: u32,
    #[mini(skip_serializing_if = "Option::is_none")]
    friend: Option<String>,
}
}

to_value and from_value

Miniserde doesn’t ship from_value/to_value. Midiserde does — the miniserde equivalent of serde_json::from_value and serde_json::to_value:

#![allow(unused)]
fn main() {
use midiserde::{json, from_value, to_value, Deserialize, Serialize};

#[derive(Deserialize, Serialize)]
struct Dog {
    name: String,
    breed: String,
}

// Two-stage: parse to Value, then deserialize to concrete type
let value: miniserde::json::Value =
    json::from_str(r#"{"name": "Rex", "breed": "Golden Retriever"}"#)?;
let dog: Dog = from_value(&value)?;

// Serialize to Value for inspection or further processing
let value = to_value(&dog);
}

Useful for extra-field capture, polymorphic buffers, and conditional deserialization.

Built-in with modules

Ready-made adapters for common types. Enable via features:

ModuleFeatureTypeJSON representation
midiserde::with::base64base64Vec<u8>Base64 string
midiserde::with::rfc3339chronochrono::DateTime<Utc>RFC 3339 string, e.g. "2024-02-21T14:30:00Z"
midiserde::with::timestampchronochrono::DateTime<Utc>Unix timestamp (seconds)
midiserde::with::time_deltachronochrono::TimeDelta[seconds, nanoseconds] array (chrono-compatible)
[dependencies]
midiserde = { version = "0.1", features = ["base64", "chrono"] }
#![allow(unused)]
fn main() {
#[derive(Deserialize, Serialize)]
struct Event {
    #[mini(with = "midiserde::with::rfc3339")]
    created_at: chrono::DateTime<chrono::Utc>,
}

#[derive(Deserialize, Serialize)]
struct Payload {
    #[mini(with = "midiserde::with::base64")]
    data: Vec<u8>,
}

#[derive(Deserialize, Serialize)]
struct Config {
    #[mini(with = "midiserde::with::time_delta")]
    timeout: chrono::TimeDelta,  // JSON: [30, 0] for 30 seconds
}
}

Features

FeatureDefaultDescription
macrosyesEnable Deserialize and Serialize derive macros
base64-midiserde::with::base64 for Vec<u8>
chrono-rfc3339, timestamp, time_delta for chrono types
full-All optional features (handy for tests)

License

Apache-2.0