Creare un custom deserializer in serde e sqlx
Lo scenario è questo:
- web service che usa Axum, sqlx e serde (ovviamente)
- dal client arriva un JSON in post che ha un campo così --> "ag_utente_fk": "1"
- ma nella struct è mappato così --> pub ag_utente_fk: i32
In Rust questa cosa non è concessa, cioè non fa conversioni automatiche stile PHP/Javascript.
Abbiamo due soluzioni.
Una è di forzare lato client l'invio di un intero; il problema è che potreste non essere voi gli sviluppatori del frontend, e potreste non sapere neanche da dove vi arriva il dato (cURL, React, Angular, ecc).
La seconda soluzione, quella che sto adattoando, è creare un costum deserializer in cui facciamo la conversione noi.
Vi propongo quindi la mia struct con la funzione custom:
#[derive(Serialize, FromRow, Deserialize)]
pub struct Anagrafica {
pub ag_id: Option<i32>,
pub ag_denominazione: Option<String>,
pub ag_alias: Option<String>,
pub ag_email: Option<String>,
pub ag_piva: Option<String>,
pub ag_cf: Option<String>,
pub ag_pec: Option<String>,
pub ag_codice_ident: Option<String>,
pub ag_telefono: Option<String>,
pub ag_indirizzo: Option<String>,
pub ag_cap: Option<String>,
pub ag_citta: Option<String>,
pub ag_paese: Option<String>,
pub ag_web: Option<String>,
pub ag_logo: Option<String>,
pub ag_note: Option<String>,
#[serde(deserialize_with = "deserialize_i32_from_string")]
pub ag_utente_fk: i32,
pub ag_provincia: Option<String>,
pub ag_data_aggiunta: Option<NaiveDateTime>,
pub ag_data_modifica: Option<NaiveDateTime>,
}
fn deserialize_i32_from_string<'de, D>(deserializer: D) -> Result<i32, D::Error>
where
D: serde::Deserializer<'de>,
{
use serde::de::{Error, Unexpected};
use std::str::FromStr;
let value = serde_json::Value::deserialize(deserializer)?;
match value {
serde_json::Value::Number(n) => n
.as_i64()
.map(|v| v as i32)
.ok_or_else(|| Error::invalid_type(Unexpected::Other("number"), &"integer")),
serde_json::Value::String(s) => i32::from_str(&s)
.map_err(|_| Error::invalid_type(Unexpected::Str(&s), &"integer string")),
_ => Err(Error::invalid_type(
Unexpected::Other("non-integer"),
&"integer or string integer",
)),
}
}
In questo modo viene accettato sia "1" che 1.
Enjoy!
rust serde axum sqlx
Commentami!