---
title: "Serde Rust: Guia Completo de Serialização JSON | Rust Brasil"
url: "https://rustlang.com.br/artigos/serde-guia-completo/"
markdown_url: "https://rustlang.com.br/artigos/serde-guia-completo.MD"
description: "Guia completo do Serde em Rust: JSON, TOML, YAML e MessagePack. Custom serializers, derive e dicas de performance."
date: "2026-02-23"
author: "Equipe Rust Brasil"
---

# Serde Rust: Guia Completo de Serialização JSON | Rust Brasil

Guia completo do Serde em Rust: JSON, TOML, YAML e MessagePack. Custom serializers, derive e dicas de performance.


## Introdução

**Serde** (abreviação de **Ser**ialize + **De**serialize) é a biblioteca de serialização mais importante do ecossistema Rust. Ela fornece um framework genérico e de alta performance para converter estruturas de dados Rust de e para diversos formatos como JSON, TOML, YAML, MessagePack, Bincode e muitos outros.

A magia do Serde está na sua arquitetura: o framework é separado dos formatos. Você define como seus tipos se serializam uma vez (geralmente via `#[derive]`), e isso funciona automaticamente com qualquer formato suportado. Essa separação permite que novos formatos sejam adicionados sem alterar o código das suas structs.

O Serde é usado por praticamente toda crate relevante do ecossistema Rust — de frameworks web como Axum e Actix até ferramentas de configuração, bancos de dados e ferramentas CLI.

## Dependências no Cargo.toml

```toml
[dependencies]
serde = { version = "1", features = ["derive"] }

# Formatos (adicione conforme a necessidade)
serde_json = "1"      # JSON
toml = "0.8"           # TOML
serde_yaml = "0.9"     # YAML
rmp-serde = "1"        # MessagePack
bincode = "1"          # Bincode (binário compacto)
csv = "1"              # CSV
```

## Derive Básico: Serialize e Deserialize

A forma mais comum de usar o Serde é com derives:

```rust
use serde::{Serialize, Deserialize};

#[derive(Debug, Serialize, Deserialize)]
struct Usuario {
    nome: String,
    email: String,
    idade: u32,
    ativo: bool,
}

fn main() {
    let usuario = Usuario {
        nome: "Maria Silva".to_string(),
        email: "maria@exemplo.com".to_string(),
        idade: 28,
        ativo: true,
    };

    // Serializar para JSON
    let json = serde_json::to_string_pretty(&usuario).unwrap();
    println!("{json}");

    // Deserializar de JSON
    let de_volta: Usuario = serde_json::from_str(&json).unwrap();
    println!("{:?}", de_volta);
}
```

Saída:

```json
{
  "nome": "Maria Silva",
  "email": "maria@exemplo.com",
  "idade": 28,
  "ativo": true
}
```

## Atributos do Serde

O Serde oferece dezenas de atributos para personalizar a serialização. Aqui estão os mais importantes:

### Renomear Campos

```rust
use serde::{Serialize, Deserialize};

#[derive(Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
struct ConfigServidor {
    porta_http: u16,          // -> "portaHttp"
    max_conexoes: u32,        // -> "maxConexoes"
    tempo_timeout: u64,       // -> "tempoTimeout"
}

// Outras opções: "snake_case", "SCREAMING_SNAKE_CASE",
//                "kebab-case", "PascalCase"
```

### Campos Opcionais e Valores Padrão

```rust
use serde::{Serialize, Deserialize};

#[derive(Serialize, Deserialize)]
struct Config {
    host: String,

    #[serde(default = "porta_padrao")]
    porta: u16,

    #[serde(skip_serializing_if = "Option::is_none")]
    descricao: Option<String>,

    #[serde(default)]
    debug: bool,
}

fn porta_padrao() -> u16 {
    8080
}
```

### Flatten e Rename

```rust
use serde::{Serialize, Deserialize};

#[derive(Serialize, Deserialize)]
struct Endereco {
    rua: String,
    cidade: String,
    estado: String,
}

#[derive(Serialize, Deserialize)]
struct Pessoa {
    nome: String,

    #[serde(rename = "email_principal")]
    email: String,

    #[serde(flatten)]
    endereco: Endereco,
}
```

O `flatten` "achata" os campos de `Endereco` no nível superior:

```json
{
  "nome": "João",
  "email_principal": "joao@ex.com",
  "rua": "Rua A, 123",
  "cidade": "São Paulo",
  "estado": "SP"
}
```

### Enums com Tags

```rust
use serde::{Serialize, Deserialize};

// Tag externa (padrão)
#[derive(Serialize, Deserialize)]
enum Evento {
    UsuarioCriado { id: u64, nome: String },
    PedidoRealizado { id: u64, valor: f64 },
    PagamentoRecebido { pedido_id: u64 },
}
// {"UsuarioCriado": {"id": 1, "nome": "Ana"}}

// Tag interna
#[derive(Serialize, Deserialize)]
#[serde(tag = "tipo")]
enum EventoInterno {
    UsuarioCriado { id: u64, nome: String },
    PedidoRealizado { id: u64, valor: f64 },
}
// {"tipo": "UsuarioCriado", "id": 1, "nome": "Ana"}

// Tag adjacente
#[derive(Serialize, Deserialize)]
#[serde(tag = "tipo", content = "dados")]
enum EventoAdjacente {
    UsuarioCriado { id: u64, nome: String },
    PedidoRealizado { id: u64, valor: f64 },
}
// {"tipo": "UsuarioCriado", "dados": {"id": 1, "nome": "Ana"}}

// Sem tag (untagged)
#[derive(Serialize, Deserialize)]
#[serde(untagged)]
enum Valor {
    Inteiro(i64),
    Texto(String),
    Lista(Vec<String>),
}
// Tenta deserializar cada variante em ordem
```

## Multi-formato: O Mesmo Struct em Vários Formatos

Uma das maiores forças do Serde é que o mesmo derive funciona com qualquer formato:

```rust
use serde::{Serialize, Deserialize};

#[derive(Debug, Serialize, Deserialize)]
struct Configuracao {
    nome: String,
    versao: String,
    porta: u16,
    recursos: Vec<String>,
}

fn main() {
    let config = Configuracao {
        nome: "meu-app".to_string(),
        versao: "1.0.0".to_string(),
        porta: 8080,
        recursos: vec!["auth".to_string(), "cache".to_string()],
    };

    // JSON
    let json = serde_json::to_string_pretty(&config).unwrap();
    println!("=== JSON ===\n{json}\n");

    // TOML
    let toml_str = toml::to_string_pretty(&config).unwrap();
    println!("=== TOML ===\n{toml_str}\n");

    // YAML
    let yaml = serde_yaml::to_string(&config).unwrap();
    println!("=== YAML ===\n{yaml}\n");

    // MessagePack (binário)
    let msgpack = rmp_serde::to_vec(&config).unwrap();
    println!("=== MessagePack ===\n{} bytes\n", msgpack.len());

    // Deserializar de volta (qualquer formato)
    let de_json: Configuracao = serde_json::from_str(&json).unwrap();
    let de_toml: Configuracao = toml::from_str(&toml_str).unwrap();
    let de_yaml: Configuracao = serde_yaml::from_str(&yaml).unwrap();
    let de_msgpack: Configuracao = rmp_serde::from_slice(&msgpack).unwrap();

    println!("{:?}", de_json);
    println!("{:?}", de_toml);
    println!("{:?}", de_yaml);
    println!("{:?}", de_msgpack);
}
```

## Serialização Customizada

Para casos onde o derive não é suficiente, você pode implementar serialização personalizada:

### Custom Serialize para um Campo

```rust
use serde::{Serialize, Deserialize, Serializer, Deserializer};
use chrono::NaiveDateTime;

#[derive(Debug, Serialize, Deserialize)]
struct LogEntry {
    mensagem: String,

    #[serde(serialize_with = "serializar_data", deserialize_with = "deserializar_data")]
    timestamp: NaiveDateTime,
}

fn serializar_data<S>(data: &NaiveDateTime, serializer: S) -> Result<S::Ok, S::Error>
where
    S: Serializer,
{
    let formatado = data.format("%d/%m/%Y %H:%M:%S").to_string();
    serializer.serialize_str(&formatado)
}

fn deserializar_data<'de, D>(deserializer: D) -> Result<NaiveDateTime, D::Error>
where
    D: Deserializer<'de>,
{
    let s = String::deserialize(deserializer)?;
    NaiveDateTime::parse_from_str(&s, "%d/%m/%Y %H:%M:%S")
        .map_err(serde::de::Error::custom)
}
```

### Implementação Manual Completa

```rust
use serde::{Serialize, Serializer};
use serde::ser::SerializeStruct;

struct Cor {
    r: u8,
    g: u8,
    b: u8,
}

impl Serialize for Cor {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: Serializer,
    {
        // Serializar como string hexadecimal
        let hex = format!("#{:02x}{:02x}{:02x}", self.r, self.g, self.b);
        serializer.serialize_str(&hex)
    }
}

fn main() {
    let cor = Cor { r: 255, g: 128, b: 0 };
    let json = serde_json::to_string(&cor).unwrap();
    println!("{json}"); // "#ff8000"
}
```

### Deserializar de Múltiplos Formatos

```rust
use serde::{Deserialize, Deserializer};

#[derive(Debug, Deserialize)]
struct Config {
    #[serde(deserialize_with = "string_ou_numero")]
    porta: u16,
}

fn string_ou_numero<'de, D>(deserializer: D) -> Result<u16, D::Error>
where
    D: Deserializer<'de>,
{
    #[derive(Deserialize)]
    #[serde(untagged)]
    enum StringOuNumero {
        Str(String),
        Num(u16),
    }

    match StringOuNumero::deserialize(deserializer)? {
        StringOuNumero::Str(s) => s.parse().map_err(serde::de::Error::custom),
        StringOuNumero::Num(n) => Ok(n),
    }
}

fn main() {
    // Ambos funcionam:
    let c1: Config = serde_json::from_str(r#"{"porta": 8080}"#).unwrap();
    let c2: Config = serde_json::from_str(r#"{"porta": "8080"}"#).unwrap();
    println!("{:?} {:?}", c1, c2);
}
```

## Trabalhando com JSON Dinâmico

Quando você não conhece a estrutura do JSON antecipadamente:

```rust
use serde_json::Value;

fn main() {
    let json_str = r#"{
        "nome": "Ana",
        "idade": 30,
        "hobbies": ["leitura", "programação"],
        "endereco": {
            "cidade": "São Paulo"
        }
    }"#;

    // Parse para Value genérico
    let valor: Value = serde_json::from_str(json_str).unwrap();

    // Acessar campos
    println!("Nome: {}", valor["nome"]);
    println!("Cidade: {}", valor["endereco"]["cidade"]);

    // Verificar tipo
    if let Some(hobbies) = valor["hobbies"].as_array() {
        for hobby in hobbies {
            println!("Hobby: {}", hobby.as_str().unwrap_or_default());
        }
    }

    // Construir JSON com macro
    let novo = serde_json::json!({
        "status": "ok",
        "dados": {
            "total": 42,
            "itens": ["a", "b", "c"]
        }
    });
    println!("{}", serde_json::to_string_pretty(&novo).unwrap());
}
```

## Performance

O Serde é extremamente performático. Benchmarks comparativos:

| Operação (JSON) | serde_json (Rust) | Jackson (Java) | json (Python) |
|---|---|---|---|
| Serialização | ~500 MB/s | ~300 MB/s | ~50 MB/s |
| Deserialização | ~400 MB/s | ~250 MB/s | ~40 MB/s |

### Dicas de Performance

1. **Use `&str` ao invés de `String`** quando possível na deserialização:

```rust
use serde::Deserialize;

// Rápido: zero-copy para strings simples
#[derive(Deserialize)]
struct Rapido<'a> {
    #[serde(borrow)]
    nome: &'a str,
}

// Mais lento: aloca String
#[derive(Deserialize)]
struct Normal {
    nome: String,
}
```

2. **Use `serde_json::from_reader`** para streams ao invés de carregar tudo na memória:

```rust
use std::fs::File;
use std::io::BufReader;

fn ler_config() -> Result<Config, Box<dyn std::error::Error>> {
    let file = File::open("config.json")?;
    let reader = BufReader::new(file);
    let config = serde_json::from_reader(reader)?;
    Ok(config)
}
```

3. **Use Bincode ou MessagePack** para comunicação entre serviços (muito mais rápido que JSON):

```rust
use serde::{Serialize, Deserialize};

#[derive(Serialize, Deserialize)]
struct Mensagem {
    id: u64,
    conteudo: String,
}

fn main() {
    let msg = Mensagem { id: 1, conteudo: "teste".to_string() };

    // Bincode: ~10x mais rápido que JSON
    let bytes = bincode::serialize(&msg).unwrap();
    println!("Bincode: {} bytes", bytes.len());

    // MessagePack: compacto e cross-language
    let mp = rmp_serde::to_vec(&msg).unwrap();
    println!("MsgPack: {} bytes", mp.len());

    // JSON: legível, mas maior
    let json = serde_json::to_vec(&msg).unwrap();
    println!("JSON: {} bytes", json.len());
}
```

## Integração com Frameworks Web

O Serde é a base de serialização para todos os frameworks web em Rust:

```rust
// Axum: extractors e respostas JSON automáticas
use axum::{extract::Json, routing::post, Router};
use serde::{Deserialize, Serialize};

#[derive(Deserialize)]
struct Input {
    texto: String,
}

#[derive(Serialize)]
struct Output {
    resultado: String,
    tamanho: usize,
}

async fn processar(Json(input): Json<Input>) -> Json<Output> {
    Json(Output {
        tamanho: input.texto.len(),
        resultado: input.texto.to_uppercase(),
    })
}
```

Para mais sobre Axum, veja [Axum vs Actix Web](/artigos/axum-vs-actix/).

## Padrões e Boas Práticas

1. **Sempre use `#[serde(rename_all = "camelCase")]`** ao expor APIs JSON para frontends JavaScript
2. **Use `#[serde(skip_serializing_if = "Option::is_none")]`** para campos opcionais
3. **Use `#[serde(default)]`** para backward compatibility ao adicionar novos campos
4. **Separe structs de entrada e saída** — não use o mesmo struct para request e response
5. **Use `#[serde(deny_unknown_fields)]`** quando quiser ser estrito na validação

```rust
use serde::Deserialize;

#[derive(Deserialize)]
#[serde(deny_unknown_fields)]
struct RequestEstrito {
    nome: String,
    email: String,
    // Qualquer campo não listado aqui causará erro na deserialização
}
```

## Conclusão

O Serde é uma peça fundamental do ecossistema Rust. Sua arquitetura de zero-cost abstractions permite serialização e deserialização de alta performance em qualquer formato, com uma API ergonômica baseada em derives. Dominar o Serde e seus atributos é essencial para qualquer desenvolvedor Rust.

## Veja Também

- [Receita: Parse JSON em Rust](/receitas/parse-json/)
- [Receita: Serializar JSON em Rust](/receitas/serializar-json/)
- [Receita: Ler TOML](/receitas/ler-toml/)
- [Receita: Ler YAML](/receitas/ler-yaml/)
- [Diesel vs SQLx: ORM vs Query Builder](/artigos/diesel-vs-sqlx/)
- [Reqwest: HTTP Client Completo em Rust](/artigos/reqwest-guia-completo/)
- [Error Handling: anyhow vs thiserror vs miette](/artigos/error-handling-libs/)

---

Se você trabalha com serialização de dados em outras linguagens, confira também:

- <a href="https://golang.com.br/" target="_blank" rel="noopener" onclick="umami.track('portfolio-site-click', { destination: 'golang.com.br' })">Go encoding/json: serialização JSON com a standard library de Go</a>
- <a href="https://python.dev.br/" target="_blank" rel="noopener" onclick="umami.track('portfolio-site-click', { destination: 'python.dev.br' })">Python json e Pydantic: validação e serialização de dados em Python</a>
- <a href="https://kotlin.dev.br/" target="_blank" rel="noopener" onclick="umami.track('portfolio-site-click', { destination: 'kotlin.dev.br' })">Kotlin Serialization: serialização multiplataforma type-safe em Kotlin</a>
