Невозможно записать поле в документ MongoDB при использовании атрибута serde skip_serialize в Rust

Я хочу иметь возможность записывать поле в документ mongo, но избегать его сериализации при передаче объекта в качестве ответа клиенту.

Я пробовал использовать #[serde(skip_serializing)]. Однако он работает не так, как я предполагал, что, как мне кажется, связано с тем, что при преобразовании Region с использованием _ 3_ из библиотеки mongo, он сериализуется с атрибутом skip, так как он также использует сериализатор serde. Пожалуйста, поправьте меня, если я ошибаюсь.

У меня есть две структуры, определенные следующим образом:

#[serde(rename_all = "camelCase")]
#[derive(Serialize, Deserialize, Debug)]
pub struct Region {
    #[serde(alias = "_id")]
    pub id: Option<i32>,
    pub name: String,
    ...
    pub areas: Vec<Area>,
    ...
}

#[serde(rename_all = "camelCase")]
#[derive(Serialize, Deserialize, Debug)]
pub struct Area {
    pub id: Option<String>,
    pub name: String,
    #[serde(skip_serializing)]
    pub ext_api_id: Option<String>,
}

При проверке документа вручную после вставки поле extApiId отсутствует.

Я попытался вручную добавить его в сгенерированный документ по ключу (что-то вроде document.insert("_id", id)), но не смог найти и понять, как получить доступ к полям в массиве с помощью ключевых строк. В идеале я бы все равно не хотел этого делать.

Вот пример кода из моей функции обновления:

pub async fn update(&self, id: i32, mut region: Region) -> Result<i32, Box<dyn Error>> {
        for mut area in &mut region.areas {
            area.id = Some(get_slug_from_name(area.name.clone()));
        }

        let mut document = to_document(&region)?;
        document.remove("id");
        document.insert("_id", id);

        self.collection
            .update_one(
                doc! { "_id": id },
                document,
                UpdateOptions::builder().upsert(Some(true)).build(),
            )
            .await?;
        Ok(id)
    }

Есть предложения, как обойти эту проблему? Я думал о том, чтобы иметь Area и InsertableArea без атрибута field, для которого, я полагаю, мне также понадобится InsertableRegion.


person Luis Octavio Lomeli Navarrete    schedule 09.03.2021    source источник


Ответы (1)


Очевидно, что если вы попросите serde не сериализовать это поле, поле ни в коем случае не будет сериализовано.

Если в одном из ваших данных есть дополнительное поле, это НЕ те же данные, поэтому вам нужны две структуры.

Пример грамотного использования serde flatten:

#[serde(rename_all = "camelCase")]
#[derive(Serialize, Deserialize, Debug)]
pub struct Area {
    pub id: Option<String>,
    pub name: String,
}

#[serde(rename_all = "camelCase")]
#[derive(Serialize, Deserialize, Debug)]
pub struct AreaAPI {
    #[serde(flatten)]
    pub area: Area,
    pub ext_api_id: Option<String>,
}

Другим решением может быть использование skip_serializing_none, но я не думаю, что это лучшее решение для вашего случая использования.

person Stargateur    schedule 09.03.2021
comment
Да, я думаю, что моя проблема на самом деле заключается в библиотеке BSON, использующей тот же сериализатор, которого я сначала не ожидал, но это имеет смысл, учитывая известность serde. Я пока воспользуюсь несколькими структурами с плоским маршрутом. Спасибо! - person Luis Octavio Lomeli Navarrete; 09.03.2021