serde-metaform

Crates.ioserde-metaform
lib.rsserde-metaform
version1.0.1
created_at2025-10-07 11:01:12.576729+00
updated_at2025-10-09 09:55:08.941428+00
descriptionForm encoder for Meta (Facebook/WhatsApp/Graph API) batch requests. Serializes values to JSON then URL-encodes them, matching Meta’s quirky request body format.
homepage
repositoryhttps://github.com/veecore/serde-metaform
max_upload_size
id1871515
size96,275
Victor (veecore)

documentation

https://docs.rs/serde-metaform

README

serde_metaform

Crates.io Docs.rs CI

A high-performance serde serializer for the hybrid "Form + JSON" encoding format used by APIs like Meta’s (WhatsApp Business, Instagram Messaging, etc.).

⚠️ Warning: This is not a standard application/x-www-form-urlencoded serializer. It produces a specialized encoding where values are JSON-encoded before being percent-encoded. Do not use this for ordinary HTML form submissions.


🧩 What Is “Form + JSON” Encoding?

This format looks like a typical form payload (key=value&key2=value2), but each value is actually JSON.

  1. Top-level data is represented as key–value pairs.
  2. Keys are percent-encoded strings.
  3. Values are serialized as JSON strings, then the entire JSON string is percent-encoded.

This hybrid structure allows complex nested JSON to be transmitted in APIs that only accept form-like bodies.

Example

Standard form encoding:

user_id=123&tags=rust&tags=serde

Form + JSON encoding:

user_id=123&
profile=%7B%22username%22%3A%22jdoe%22%2C%22tags%22%3A%5B%22rust%22%2C%22serde%22%5D%7D

The profile value above is the percent-encoded JSON string:

{"username":"jdoe","tags":["rust","serde"]}

🚀 Features

  • Single-Pass Serialization: No intermediate serde_json::Value, no double parsing.
  • Zero-Copy: Streams directly into the output writer with minimal allocation.
  • Full serde Integration: Works out of the box with #[derive(Serialize)].
  • Nested Data Support: Handles structs, enums, sequences, and maps cleanly.
  • Battle-Tested: Used internally in whatsapp-business-rs.

🧠 Usage

Add to your Cargo.toml:

[dependencies]
serde = { version = "1.0", features = ["derive"] }
serde_metaform = "1"

Then simply serialize your data:

use serde::Serialize;
use serde_metaform::to_string;

#[derive(Serialize)]
struct Message<'a> {
    recipient: &'a str,
    content: Content<'a>,
}

#[derive(Serialize)]
struct Content<'a> {
    #[serde(rename = "type")]
    message_type: &'a str,
    text: &'a str,
    buttons: Vec<Button<'a>>,
}

#[derive(Serialize)]
struct Button<'a> {
    id: &'a str,
    title: &'a str,
}

fn main() -> Result<(), serde_metaform::error::Error> {
    let msg = Message {
        recipient: "1234567890",
        content: Content {
            message_type: "interactive",
            text: "Choose an option:",
            buttons: vec![
                Button { id: "opt1", title: "Option 1" },
                Button { id: "opt2", title: "Option 2" },
            ],
        },
    };

    let encoded = to_string(&msg)?;
    println!("{encoded}");

    // Output (simplified, actual output is percent-encoded):
    // recipient=1234567890&
    // content={"type":"interactive","text":"Choose an option:","buttons":[{"id":"opt1","title":"Option 1"},{"id":"opt2","title":"Option 2"}]}

    Ok(())
}

⚡ Performance

serde_metaform was originally extracted from a production WhatsApp integration layer, where JSON bodies were pre-built before conversion to the hybrid format. This crate eliminates that two-step process.

Benchmark Description Mean Time Relative
json_value struct → JSON Value → form 35.5 µs 1.00×
json_pipeline struct → Bytes → form (old pipeline) 23.6 µs 1.5× faster
from_struct struct → form (serde_metaform) 14.0 µs 2.53× faster

Real-world WhatsApp payloads typically gain 35–45 % throughput improvement.

Even under realistic, non-pretty-printed conditions, serde_metaform reduces total serialization time by ~40% and cuts memory allocations roughly in half. Performance gains scale further with larger or deeply nested payloads.

📜 License

Licensed under either of:

at your option.

Commit count: 0

cargo fmt