Trabalhando com JSON usando serde_json

1. Introdução ao serde_json e Configuração Inicial

O ecossistema Serde é o framework de serialização mais utilizado em Rust, oferecendo desempenho excepcional e segurança de tipos. O serde_json é o crate responsável por serializar e desserializar dados no formato JSON, aproveitando todo o poder do sistema de tipos do Rust.

Para começar, adicione as seguintes dependências ao seu Cargo.toml:

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

A feature derive do serde permite usar macros como #[derive(Serialize, Deserialize)]. Para precisão arbitrária em números, adicione a feature arbitrary_precision ao serde_json:

serde_json = { version = "1.0", features = ["arbitrary_precision"] }

2. Serialização de Dados Rust para JSON

Vamos começar serializando estruturas Rust para JSON. O serde_json oferece funções como to_string, to_vec e to_writer:

use serde::{Serialize, Deserialize};

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

#[derive(Serialize, Deserialize, Debug)]
enum Status {
    Ativo,
    Inativo,
    Pendente,
}

fn main() {
    let usuario = Usuario {
        nome: "Alice".to_string(),
        idade: 30,
        email: "alice@exemplo.com".to_string(),
        ativo: true,
    };

    // Serialização padrão (compacta)
    let json = serde_json::to_string(&usuario).unwrap();
    println!("JSON compacto: {}", json);

    // Serialização formatada (pretty)
    let json_pretty = serde_json::to_string_pretty(&usuario).unwrap();
    println!("JSON formatado:\n{}", json_pretty);

    // Serializando para bytes
    let bytes = serde_json::to_vec(&usuario).unwrap();
    println!("Bytes: {:?}", bytes);

    // Serializando coleções
    let numeros = vec![1, 2, 3, 4, 5];
    println!("Vetor: {}", serde_json::to_string(&numeros).unwrap());

    // Serializando opcionais
    let opcional: Option<String> = Some("valor".to_string());
    println!("Optional: {}", serde_json::to_string(&opcional).unwrap());
}

3. Desserialização de JSON para Tipos Rust

A desserialização converte JSON de volta para tipos Rust. As funções principais são from_str, from_reader e from_slice:

use serde::{Deserialize, Serialize};
use std::fs::File;
use std::io::BufReader;

#[derive(Deserialize, Debug)]
struct Produto {
    id: u32,
    nome: String,
    preco: f64,
    #[serde(default)]
    descricao: Option<String>,
}

fn main() -> Result<(), serde_json::Error> {
    // Desserializando de string
    let json_str = r#"
    {
        "id": 1,
        "nome": "Notebook",
        "preco": 2500.50,
        "descricao": "Notebook de última geração"
    }"#;

    let produto: Produto = serde_json::from_str(json_str)?;
    println!("Produto: {:?}", produto);

    // Desserializando de arquivo
    let arquivo = File::open("produto.json").unwrap_or_else(|_| {
        eprintln!("Arquivo não encontrado, criando exemplo...");
        let json = r#"{"id":2,"nome":"Mouse","preco":89.90}"#;
        std::fs::write("produto.json", json).unwrap();
        File::open("produto.json").unwrap()
    });

    let reader = BufReader::new(arquivo);
    let produto_arquivo: Produto = serde_json::from_reader(reader)?;
    println!("Produto do arquivo: {:?}", produto_arquivo);

    // Tratamento de erros
    let json_invalido = r#"{"id": "nao_eh_numero"}"#;
    match serde_json::from_str::<Produto>(json_invalido) {
        Ok(p) => println!("{}", p.id),
        Err(e) => eprintln!("Erro ao desserializar: {}", e),
    }

    Ok(())
}

4. Customização com Atributos Serde

Os atributos Serde permitem controle fino sobre como os dados são serializados e desserializados:

use serde::{Serialize, Deserialize};

#[derive(Serialize, Deserialize, Debug)]
#[serde(rename_all = "camelCase")]
struct Configuracao {
    #[serde(rename = "db_host")]
    database_host: String,
    #[serde(rename = "db_port")]
    database_port: u16,
    #[serde(skip_serializing_if = "Option::is_none")]
    timeout: Option<u64>,
    #[serde(default = "default_max_conexoes")]
    max_conexoes: u32,
}

fn default_max_conexoes() -> u32 {
    10
}

#[derive(Serialize, Deserialize, Debug)]
#[serde(tag = "tipo", content = "dados")]
enum Mensagem {
    Texto(String),
    Imagem { url: String, largura: u32, altura: u32 },
    Audio { duracao: f64 },
}

fn main() {
    let config = Configuracao {
        database_host: "localhost".to_string(),
        database_port: 5432,
        timeout: Some(30),
        max_conexoes: 5,
    };

    let json = serde_json::to_string_pretty(&config).unwrap();
    println!("Configuração:\n{}", json);

    // Exemplo com flatten para achatar structs aninhadas
    #[derive(Serialize, Deserialize)]
    struct Endereco {
        rua: String,
        cidade: String,
    }

    #[derive(Serialize, Deserialize)]
    struct Pessoa {
        nome: String,
        #[serde(flatten)]
        endereco: Endereco,
    }

    let pessoa = Pessoa {
        nome: "João".to_string(),
        endereco: Endereco {
            rua: "Rua A".to_string(),
            cidade: "São Paulo".to_string(),
        },
    };

    println!("Pessoa com flatten: {}", serde_json::to_string(&pessoa).unwrap());

    // Enum com tag
    let msg = Mensagem::Texto("Olá".to_string());
    println!("Mensagem: {}", serde_json::to_string(&msg).unwrap());
}

5. Trabalhando com JSON Dinâmico e Não Tipado

Para situações onde a estrutura do JSON é desconhecida, use serde_json::Value:

use serde_json::{Value, json};

fn main() {
    // Parsing de JSON dinâmico
    let dados = r#"
    {
        "nome": "Maria",
        "idade": 28,
        "habilidades": ["Rust", "Python", "JavaScript"],
        "endereco": {
            "cidade": "Rio de Janeiro",
            "pais": "Brasil"
        }
    }"#;

    let valor: Value = serde_json::from_str(dados).unwrap();

    // Acessando dados
    if let Some(nome) = valor.get("nome") {
        println!("Nome: {}", nome);
    }

    if let Some(idade) = valor.get("idade").and_then(|v| v.as_u64()) {
        println!("Idade: {}", idade);
    }

    // Navegação em estruturas aninhadas
    if let Some(cidade) = valor["endereco"]["cidade"].as_str() {
        println!("Cidade: {}", cidade);
    }

    // Iterando sobre arrays
    if let Some(habilidades) = valor["habilidades"].as_array() {
        for (i, habilidade) in habilidades.iter().enumerate() {
            println!("Habilidade {}: {}", i + 1, habilidade);
        }
    }

    // Construção programática com macro json!
    let novo_json = json!({
        "status": "sucesso",
        "dados": {
            "usuarios": [
                {"nome": "Ana", "idade": 25},
                {"nome": "Pedro", "idade": 32}
            ],
            "total": 2
        },
        "metadados": null
    });

    println!("JSON construído: {}", serde_json::to_string_pretty(&novo_json).unwrap());

    // Modificando valores
    if let Some(obj) = novo_json.as_object() {
        println!("Chaves disponíveis: {:?}", obj.keys());
    }
}

6. Performance e Boas Práticas

Para aplicações que exigem alta performance, considere estas técnicas:

use serde::{Deserialize, Serialize};
use serde_json::value::RawValue;
use std::io::{BufWriter, Write};

#[derive(Deserialize)]
struct DadoStream {
    id: u32,
    #[serde(borrow)]
    conteudo: &' RawValue,  // Evita alocação desnecessária
}

fn processar_grande_arquivo() -> Result<(), Box<dyn std::error::Error>> {
    // Leitura streaming para arquivos grandes
    let dados = r#"{"id":1,"conteudo":"dado1"}
{"id":2,"conteudo":"dado2"}
{"id":3,"conteudo":"dado3"}"#;

    let stream = serde_json::Deserializer::from_str(dados)
        .into_iter::<DadoStream>();

    for resultado in stream {
        match resultado {
            Ok(item) => println!("Processando item {}", item.id),
            Err(e) => eprintln!("Erro no streaming: {}", e),
        }
    }

    // Escrita direta em buffer para evitar alocações
    let mut buffer = Vec::new();
    let mut writer = BufWriter::new(&mut buffer);

    let dados = vec![
        json!({"nome": "Item1", "valor": 100}),
        json!({"nome": "Item2", "valor": 200}),
    ];

    for item in &dados {
        serde_json::to_writer(&mut writer, item)?;
        writeln!(&mut writer)?;  // Adiciona nova linha entre objetos
    }

    writer.flush()?;
    println!("Buffer final: {:?}", String::from_utf8_lossy(&buffer));

    Ok(())
}

7. Casos Especiais e Interoperabilidade

use serde::{Deserialize, Serialize};
use serde_json::Number;
use std::str::FromStr;

// Números de precisão arbitrária
#[derive(Serialize, Deserialize, Debug)]
struct Transacao {
    #[serde(with = "serde_json::number")]
    valor_exato: Number,
    descricao: String,
}

// Custom serialização para datas
#[derive(Debug)]
struct DataCustomizada {
    timestamp: i64,
}

impl Serialize for DataCustomizada {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: serde::Serializer,
    {
        use serde::ser::SerializeStruct;
        let mut state = serializer.serialize_struct("DataCustomizada", 1)?;
        state.serialize_field("timestamp", &self.timestamp)?;
        state.end()
    }
}

impl<'de> Deserialize<'de> for DataCustomizada {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: serde::Deserializer<'de>,
    {
        let valor = serde_json::Value::deserialize(deserializer)?;
        let timestamp = valor["timestamp"]
            .as_i64()
            .ok_or_else(|| serde::de::Error::custom("timestamp inválido"))?;
        Ok(DataCustomizada { timestamp })
    }
}

fn main() -> Result<(), Box<dyn std::error::Error>> {
    // Precisão arbitrária
    let json_str = r#"{"valor_exato": 123456789012345678901234567890, "descricao": "teste"}"#;
    let transacao: Transacao = serde_json::from_str(json_str)?;
    println!("Transação: {:?}", transacao);

    // Re-serializando mantém precisão
    let json_saida = serde_json::to_string(&transacao)?;
    println!("JSON de saída: {}", json_saida);

    // Data customizada
    let data = DataCustomizada { timestamp: 1700000000 };
    let json_data = serde_json::to_string(&data)?;
    println!("Data serializada: {}", json_data);

    let data_desserializada: DataCustomizada = serde_json::from_str(&json_data)?;
    println!("Data desserializada: {:?}", data_desserializada);

    // Integração com logging
    let evento = json!({
        "nivel": "info",
        "mensagem": "Operação concluída",
        "dados": {
            "usuario": "admin",
            "acao": "login"
        }
    });

    println!("Evento para logging: {}", evento);

    Ok(())
}

Referências