JSON parsing and serialization with Serde in Rust.


Basic Setup

# Cargo.toml
[dependencies]
serde = { version = "1", features = ["derive"] }
serde_json = "1"
use serde::{Deserialize, Serialize};

#[derive(Debug, Deserialize)]
pub struct ExchangeInfo {
    symbols: Vec<SymbolInfo>,
}

Common Serde Attributes

Rename Fields

#[derive(Deserialize)]
#[serde(rename_all = "camelCase")]  // All fields: snake_case → camelCase
pub struct SymbolInfo {
    symbol: String,
    contract_type: String,  // Matches "contractType" in JSON
    
    #[serde(rename = "E")]  // Specific field rename
    event_time: u64,
}

Tagged Enums (for discriminated unions)

#[derive(Debug, Deserialize)]
#[serde(tag = "filterType")]  // Use "filterType" field as discriminator
pub enum Filter {
    #[serde(rename = "PRICE_FILTER")]
    PriceFilter {
        #[serde(rename = "tickSize")]
        tick_size: String,
    },

    #[serde(rename = "LOT_SIZE")]
    LotSize {
        #[serde(rename = "stepSize")]
        step_size: String,
    },

    #[serde(other)]  // Catch-all for unknown variants
    Other,
}

⚠️ Note: Don't use rename_all on enum variants with #[serde(other)]—it can cause conflicts. Use explicit #[serde(rename = "...")] instead.


Common Errors

"Missing field" Error

Usually means:

  1. Nested type missing Deserialize

    #[derive(Deserialize)]  // ← Add this!
    struct Symbol { ... }
    
  2. API returns different structure than expected

    // Debug by printing raw response:
    let text = response.text().await?;
    println!("{}", text);
    
  3. Optional field not marked as Option

    #[serde(default)]  // Use default if missing
    description: String,
    
    // OR
    description: Option<String>,  // None if missing
    

"Trait bound not satisfied"