Search code examples
rustserializationdeserializationserde

Deserialize a hex seq into an optional vec of ObjectId


I'm trying to make a custom serde helpers library, but I'm not sure how to deserialize a seq of hex into a optional Vec of ObjectId. (ObjectId is from bson::oid)

The thing is I want the Vec to be None if the hex seq length is 0. (so empty)

Thanks in advance!

pub mod option_vec_object_id_as_hex_seq {
  use bson::oid::ObjectId;
  use serde::{ser::SerializeSeq, Serializer};
  use std::result::Result;

  /// Deserializes a [`Option<Vec<ObjectId>>`] from a seq of hex
  // pub fn deserialize<'de, D>(deserializer: D) -> Result<Option<Vec<ObjectId>>, D::Error>
  // where
  //   D: Deserializer<'de>,
  // {
  //   // TODO: Convert hex seq into Option<Vec<ObjectId>>
  // }

  /// Serializes a [`Option<Vec<ObjectId>>`] as a seq of hex
  pub fn serialize<S: Serializer>(
    val: &Option<Vec<ObjectId>>,
    serializer: S,
  ) -> Result<S::Ok, S::Error> {
    match val {
      Some(val) => {
        let mut seq = serializer.serialize_seq(Some(val.len()))?;
        for e in val {
          seq.serialize_element(&e.to_hex())?;
        }
        seq.end()
      }
      None => serializer.serialize_seq(None)?.end(),
    }
  }
}

I've tried to use Visitor, but I had problem with creating a Vec.

Edit: Here is expected input:

["6397f513d5e4a64eda84aa37", "6397f513d5e4a64eda84aa39"]

For expected output:

vec![ObjectId("6397f513d5e4a64eda84aa37"), ObjectId("6397f513d5e4a64eda84aa37")]

If the input is an empty array:

[]

Output would be:

None

Solution

  • This is actually much easier than you might think. You can just deserialize Vec<ObjectId> then check if it has any items. You don't need to worry about this allocating memory on the heap since it should either be creating it with the sequence size hint or by filling an empty Vec. And since an empty Vec does not allocate memory on the heap, this can be kept on the stack in most cases.

    pub fn deserialize<'de, D, T>(deserializer: D) -> Result<Option<Vec<T>>, D::Error>
    where
        T: Deserialize<'de>,
        D: Deserializer<'de>,
    {
        let sequence = <Vec<T>>::deserialize(deserializer)?;
    
        if sequence.len() == 0 {
            return Ok(None);
        }
    
        Ok(Some(sequence))
    }
    

    Rust Playground

    If you want to use a Visitor it is just the same thing, but a bit longer to write.

    struct ItemVisitor<'a, T> {
        _phantom: PhantomData<&'a T>,
    }
    
    impl<'de, 'a: 'de, T> Visitor<'de> for ItemVisitor<'a, T>
    where
        T: Deserialize<'de>,
    {
        type Value = Option<Vec<T>>;
    
        fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
            formatter.write_str("a list of objects")
        }
    
        fn visit_seq<V>(self, mut access: V) -> Result<Self::Value, V::Error>
        where
            V: SeqAccess<'de>,
        {
            let mut items = None;
            while let Some(next) = access.next_element::<T>()? {
                match &mut items {
                    None => items = Some(vec![next]),
                    Some(values) => values.push(next),
                }
            }
    
            Ok(items)
        }
    }
    
    pub fn vec_or_none<'de, D, T>(deserializer: D) -> Result<Option<Vec<T>>, D::Error>
    where
        D: Deserializer<'de>,
        T: Deserialize<'de> + 'de,
    {
        deserializer.deserialize_seq(ItemVisitor {
            _phantom: PhantomData,
        })
    }
    

    Rust Playground