0

I want to be able to write a field into a mongo document, but avoid serializing it when passing the object as a response to the client.

I tried using #[serde(skip_serializing)]. However, it doesn't work as I intended, which I think is because when it converts Region using to_document from the mongo library, it gets serialized with the skip attribute, as it also uses the serde serializer. Please correct me if I'm wrong here.

I have two structs defined like the following:

#[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>,
}

When manually inspecting a document after an insert, there's no extApiId field.

I tried manually adding it to the generated document by key (something like document.insert("_id", id)), but I couldn't find nor figure out how to access fields within an array via key strings. Ideally, I'd like to not have to do this anyway.

Here's some example code from my update function:

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)
    }

Any suggestions on how to work around this issue? I thought about having Area and InsertableArea without the field attribute, which I suppose I'd also need a InsertableRegion for.

1 Answers1

1

Obviously if you ask serde to not serialize this field, the field will not be serialized in any case.

If one of your data have an additional field there are NOT the same data, so you need two structure.

Example of clever use of 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>,
}

Another solution could be to use skip_serializing_none but I don't think that the best solution for your use case.

Stargateur
  • 24,473
  • 8
  • 65
  • 91
  • Yeah, I think my problem is actually the BSON library using the same serializer which I didn't expect at first, but it makes sense given serde's prominency. I'll go for the multiple structs with flatten route for now. Thanks! – Luis Octavio Lomeli Navarrete Mar 09 '21 at 05:51