можно ли автоматически игнорировать поля структуры при использовании десериализации

#rust #serde

Вопрос:

Я использую rust rocket в качестве http-сервера. На стороне сервера я определяю структуру rust, подобную этой, для получения данных со стороны клиента:

 #[derive(Deserialize, Serialize)]
#[allow(non_snake_case)]
pub struct PlayRecordRequest {
    pub id: i64,
    pub title: String,
    pub url: String,
    pub mvId: i64,
    pub album: MusicAlbum,
    pub artist: Vec<Artist>,
}
 

и получите в контроллере http вот так:

 #[post("/user/v1/save-play-record", data = "<record>")]
pub fn save_play_record<'r>(
    record: Json<PlayRecordRequest>,
) -> content::Json<String> {
    info!("save play record,title:{}",record.title);
    save_play_record_impl(record);
    let res = ApiResponse {
        result: "ok".to_string(),
        ..Default::default()
    };
    let result_json = serde_json::to_string(amp;res).unwrap();
    return content::Json(result_json);
}
 

проблема в том, что, когда на стороне клиента не было некоторых полей, код запускался с ошибкой. можно ли автоматически разместить поле для десериализации, если у клиента есть поле, Десериализовать его обычно, если клиент не содержит некоторых полей, просто проигнорируйте его и не столкнитесь с ошибкой. Я читаю официальный документ серда и нахожу skip аннотацию. Но аннотацию, используемую только для обозначения поля, следует игнорировать, я хочу, чтобы структура могла автоматически соответствовать всем существующим полям или нет. можно ли так поступить?

Комментарии:

1. Я думаю, что вы ищете #[serde(default)] .

Ответ №1:

Есть два способа справиться с этим.

Во-первых, с опциями. Варианты подразумевают, что данные могут существовать или не существовать. Если данные есть null или отсутствуют, они преобразуются в Option::none значение. Вы можете сохранить отсутствие данных о сериализации, если добавите #[serde(skip_serializing_if = "Option::is_none")]

Второй вариант-применить значения по умолчанию к значению, если данные отсутствуют. Хотя, судя по вашему варианту использования, это не кажется идеальным.

Вот фрагмент кода двух случаев, которые вы можете запустить https://play.rust-lang.org/:

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

#[derive(Deserialize, Serialize, Debug)]
#[allow(non_snake_case)]
pub struct Foo {
    #[serde(skip_serializing_if = "Option::is_none")]
    pub bar: Option<i64>,
    #[serde(default = "default_baz")]
    pub baz: i64,
}

fn default_baz()-> i64{
    3
}

fn main(){
    let data = r#"{"bar": null, "baz": 1}"#;
    let v: Foo = serde_json::from_str(data).unwrap();
    println!("{:#?}", v);
    
    let s = serde_json::to_string(amp;v).unwrap();
    println!("Don't serialize None values: {:#?}", s);
    
    let data = r#"{"missing_bar": null}"#;
    let v: Foo = serde_json::from_str(data).unwrap();
    println!("{:#?}", v)
    
}