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

使用Rust将Bluesky FeedViewPost序列化为BSON存入MongoDB时,如何解决i128不支持的序列化错误?

Rust将Bluesky FeedViewPost序列化为BSON存入MongoDB时,如何解决i128不支持的序列化错误?

我太懂你这个痛点了——用Bluesky的SDK拿到FeedViewPost后,想存MongoDB却卡在i128序列化上,而且这些带i128的结构体还是第三方crate定义的,没法直接改字段属性对吧?别慌,咱们有几个可行的方案,从快速救急到长期稳健的都有,一起来看看。

方案一:快速救急——JSON中转法

这是最简单的临时解决方案,利用serde_json会自动把i128序列化为字符串的特性,先转成JSON Value再转成BSON,绕过原生i128的限制。

首先确保Cargo.toml里有serde_json依赖:

[dependencies]
# 其他依赖保持不变
serde_json = "1.0"

然后修改你的main函数里的序列化逻辑:

use mongodb::bson;
use serde_json;

#[tokio::main]
async fn main() {
    let runner = runner::PublicRunner::new().await;
    let uri = vec!["at://did:plc:z72i7hdynmk6r22z27h6tvur/app.bsky.feed.post/3lawynrmfzk2f".into()];
    let output_data = runner.get_posts(uri).await;

    // 先转成JSON Value,再转BSON
    let json_val = serde_json::to_value(&output_data)
        .expect("Unable to serialize data to JSON value");
    let doc = bson::from_value(json_val)
        .expect("Unable to convert JSON value to BSON document");

    println!("{}", doc.to_string());
}

// 你的runner模块保持不变...

这个方法优点是代码改动极小,几分钟就能跑通;缺点是可能会有一些类型隐式转换(比如某些整数变浮点数),适合快速验证功能,生产环境建议用更稳健的方案。

方案二:长期稳健——镜像结构体+手动转换

这个方案是定义和原结构体完全镜像的本地结构体,把所有i128字段替换成BSON支持的类型(比如String或Decimal128),然后实现从原类型到本地类型的转换,完全可控。

步骤1:定义镜像结构体

根据atrium_api里FeedViewPost、Object、Ipld的结构,定义对应的本地结构体,替换i128类型:

use atrium_api::types::{FeedViewPost, Object, Ipld};
use mongodb::bson::Decimal128;
use serde::{Serialize, Deserialize};
use std::collections::BTreeMap;

// 自定义Ipld,把I128换成Decimal128(保留数值精度)
#[derive(Debug, Serialize, Deserialize)]
enum MyIpld {
    Null,
    Bool(bool),
    Integer(i64),
    Float(f64),
    String(String),
    Bytes(Vec<u8>),
    Array(Vec<MyIpld>),
    Object(BTreeMap<String, MyIpld>),
    I128(Decimal128), // 用Decimal128存储i128,支持数值操作
}

// 实现从原Ipld到MyIpld的转换
impl From<&Ipld> for MyIpld {
    fn from(ipld: &Ipld) -> Self {
        match ipld {
            Ipld::Null => MyIpld::Null,
            Ipld::Bool(b) => MyIpld::Bool(*b),
            Ipld::Integer(i) => MyIpld::Integer(*i),
            Ipld::Float(f) => MyIpld::Float(*f),
            Ipld::String(s) => MyIpld::String(s.clone()),
            Ipld::Bytes(b) => MyIpld::Bytes(b.clone()),
            Ipld::Array(arr) => MyIpld::Array(arr.iter().map(MyIpld::from).collect()),
            Ipld::Object(obj) => MyIpld::Object(
                obj.iter().map(|(k, v)| (k.clone(), MyIpld::from(v))).collect()
            ),
            Ipld::I128(i) => {
                let decimal = Decimal128::from_i128(*i)
                    .unwrap_or_else(|_| Decimal128::from_str(&i.to_string()).unwrap());
                MyIpld::I128(decimal)
            }
            // 处理其他可能的变体
            _ => MyIpld::String(format!("Unsupported Ipld variant: {:?}", ipld)),
        }
    }
}

// 自定义Object结构体
#[derive(Debug, Serialize, Deserialize)]
struct MyObject {
    r#type: String,
    extra_data: MyIpld,
    did: String,
    uri: String,
    // 复制原Object的所有其他字段,保持类型一致
}

impl From<&Object> for MyObject {
    fn from(obj: &Object) -> Self {
        MyObject {
            r#type: obj.r#type.clone(),
            extra_data: MyIpld::from(&obj.extra_data),
            did: obj.did.clone(),
            uri: obj.uri.clone(),
            // 其他字段依次复制,比如cid、created_at等
        }
    }
}

// 自定义FeedViewPost结构体
#[derive(Debug, Serialize, Deserialize)]
struct MyFeedViewPost {
    post: MyObject,
    author: MyObject, // 假设author也是Object类型,替换成MyObject
    // 其他字段比如reply、like_count等,都换成对应的镜像类型
}

impl From<&FeedViewPost> for MyFeedViewPost {
    fn from(feed_post: &FeedViewPost) -> Self {
        MyFeedViewPost {
            post: MyObject::from(&feed_post.post),
            author: MyObject::from(&feed_post.author),
            // 其他字段依次转换
        }
    }
}

步骤2:转换后序列化

在main函数里把原FeedViewPost转成自定义的MyFeedViewPost,再序列化:

#[tokio::main]
async fn main() {
    let runner = runner::PublicRunner::new().await;
    let uri = vec!["at://did:plc:z72i7hdynmk6r22z27h6tvur/app.bsky.feed.post/3lawynrmfzk2f".into()];
    let output_data = runner.get_posts(uri).await;

    // 转成自定义结构体
    let my_data: Vec<MyFeedViewPost> = output_data.iter().map(MyFeedViewPost::from).collect();
    // 序列化自定义结构体到BSON
    let doc = bson::to_document(&my_data)
        .expect("Unable to serialize data to a BSON document");

    println!("{}", doc.to_string());
}

这个方案的优点是完全可控,类型转换透明,生产环境用起来放心;缺点是需要维护镜像结构体,如果atrium_api更新了结构体字段,你需要同步更新本地的镜像结构体,避免数据丢失。

方案三:高级玩法——Serde远程派生

如果不想手动维护镜像结构体,可以用Serde的remote特性,直接给第三方类型注入自定义序列化逻辑,不需要修改原crate的代码。

步骤1:添加Serde依赖

确保Cargo.toml里Serde启用了remote特性:

[dependencies]
serde = { version = "1.0", features = ["derive", "remote"] }

步骤2:远程派生自定义序列化

use atrium_api::types::{Ipld, Object};
use mongodb::bson::Decimal128;
use serde::{Serialize, Deserialize};

// 针对Ipld做远程派生,自定义I128的序列化
#[derive(Serialize, Deserialize)]
#[serde(remote = "Ipld")]
enum LocalIpld {
    Null,
    Bool(bool),
    Integer(i64),
    Float(f64),
    String(String),
    Bytes(Vec<u8>),
    Array(Vec<Ipld>),
    Object(std::collections::BTreeMap<String, Ipld>),
    #[serde(serialize_with = "serialize_i128")]
    I128(i128),
}

// 自定义i128序列化函数,转成Decimal128
fn serialize_i128<S>(value: &i128, serializer: S) -> Result<S::Ok, S::Error>
where
    S: serde::Serializer,
{
    let decimal = Decimal128::from_i128(*value)
        .map_err(|e| serde::ser::Error::custom(format!("Failed to convert i128 to Decimal128: {}", e)))?;
    decimal.serialize(serializer)
}

// 针对Object做远程派生,使用自定义的Ipld序列化
#[derive(Serialize, Deserialize)]
#[serde(remote = "Object")]
struct LocalObject {
    r#type: String,
    #[serde(with = "LocalIpld")]
    extra_data: Ipld,
    did: String,
    uri: String,
    // 其他字段保持和原Object一致
}

步骤3:序列化时指定用远程派生逻辑

在序列化时,用#[serde(with = "LocalObject")]指定对Object类型使用自定义逻辑,不过这个方法需要在序列化的根类型上添加属性,如果根类型是Vec<FeedViewPost>,可能需要对FeedViewPost也做远程派生,相对复杂一些,适合对Serde特性熟悉的开发者。

总结

  • 快速验证用JSON中转法,代码改动最小;
  • 生产环境优先用镜像结构体法,可控性强;
  • 追求极致优雅用Serde远程派生,适合Serde熟练用户。

内容来源于stack exchange

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.08 08:04:33