You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

在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"}文档
  • 若你之前的Other是按枚举默认格式({"Other": {"$oid": "xxx"}})存储,也能正确反序列化

内容的提问来源于stack exchange,提问作者wetito

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.12 20:05:30