在MongoDB中存储自定义Rust枚举遇Unknown Variant错误求助
解决MongoDB中Rust枚举Plan的Other变体反序列化"unknown variant"错误
问题原因
Free/Pro作为单元变体,默认会被序列化为字符串,反序列化时能被正确识别;但Other带ObjectId字段,若Bson存储格式与反序列化逻辑不匹配(比如存为ObjectId类型、带$oid的文档或字符串,而你的From<Bson>未处理这些情况),就会触发"unknown variant"错误。
解决方案
通过自定义TryFrom<Bson>和Into<Bson>实现,覆盖所有可能的存储格式,同时配合serde属性完成序列化反序列化:
1. 调整枚举定义与serde属性
use mongodb::bson::{oid::ObjectId, de::Error as BsonDeError, Document}; use serde::{Serialize, Deserialize}; #[derive(Debug, Clone, Serialize, Deserialize)] #[serde(try_from = "Bson", into = "Bson")] pub enum Plan { Free, Pro, Other(ObjectId), }
2. 实现TryFrom(反序列化逻辑)
覆盖字符串、ObjectId、MongoDB标准$oid文档、枚举默认文档格式等情况:
impl TryFrom<Bson> for Plan { type Error = BsonDeError; fn try_from(bson: Bson) -> Result<Self, Self::Error> { match bson { // 处理Free/Pro的字符串形式,以及Other的ObjectId字符串 Bson::String(s) => match s.as_str() { "Free" => Ok(Plan::Free), "Pro" => Ok(Plan::Pro), oid_str => ObjectId::parse_str(oid_str) .map(Plan::Other) .map_err(|e| BsonDeError::custom(format!("无效的ObjectId: {}", e))), }, // 直接处理ObjectId类型 Bson::ObjectId(oid) => Ok(Plan::Other(oid)), // 处理MongoDB的ObjectId文档格式({"$oid": "xxx"})或枚举默认文档格式({"Other": ...}) Bson::Document(doc) => { // 先判断是否是单独的ObjectId文档 if let Some(oid_str) = doc.get_str("$oid").ok() { let oid = ObjectId::parse_str(oid_str)?; return Ok(Plan::Other(oid)); } // 处理枚举的键值对文档格式 if doc.len() != 1 { return Err(BsonDeError::custom("Plan文档格式无效")); } let (key, value) = doc.into_iter().next().unwrap(); match key.as_str() { "Free" => Ok(Plan::Free), "Pro" => Ok(Plan::Pro), "Other" => { let oid = match value { Bson::ObjectId(o) => o, Bson::String(s) => ObjectId::parse_str(s)?, Bson::Document(d) => { let oid_str = d.get_str("$oid")?; ObjectId::parse_str(oid_str)? } _ => return Err(BsonDeError::custom("Other字段值格式无效")), }; Ok(Plan::Other(oid)) } _ => Err(BsonDeError::unknown_variant(key.as_str(), &["Free", "Pro", "Other"])), } } // 其他不支持的Bson类型 other => Err(BsonDeError::custom(format!( "Plan不支持的Bson类型: {:?}", other ))), } } }
3. 实现Into(序列化逻辑)
将变体转换为MongoDB友好的Bson类型:
impl Into<Bson> for Plan { fn into(self) -> Bson { match self { Plan::Free => Bson::String("Free".to_string()), Plan::Pro => Bson::String("Pro".to_string()), Plan::Other(oid) => Bson::ObjectId(oid), } } }
验证说明
- 此实现兼容以下存储格式:
- Free/Pro存储为字符串
"Free"/"Pro" - Other存储为ObjectId类型、ObjectId字符串、或MongoDB标准的
{"$oid": "xxx"}文档
- Free/Pro存储为字符串
- 若你之前的Other是按枚举默认格式(
{"Other": {"$oid": "xxx"}})存储,也能正确反序列化
内容的提问来源于stack exchange,提问作者wetito
相关产品推荐
相关产品推荐

