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

如何用Actix-web、Serde、Chrono将特定ISO 8601时间/时长序列化为Rust结构体?

实现自定义ISO 8601格式到Rust结构体的序列化/反序列化

完全可行,你需要通过Serde的自定义序列化/反序列化逻辑处理这种复合ISO格式,结合Chrono解析时间和时长部分,以下是具体实现方案:

1. 依赖配置

首先在Cargo.toml中添加所需依赖,确保Chrono启用必要特性:

[dependencies]
actix-web = "4"
serde = { version = "1", features = ["derive"] }
chrono = { version = "0.4", features = ["serde", "fixed-offset", "format"] }

2. 定义核心结构体

先定义存储激活时间和时长的结构体,以及用于接收请求的外层结构体:

use chrono::{DateTime, Duration, FixedOffset};
use serde::{Deserialize, Serialize};

#[derive(Debug, Serialize, Deserialize)]
pub struct ActivationRequest {
    #[serde(serialize_with = "serialize_schedule", deserialize_with = "deserialize_schedule")]
    pub activation_schedule: ActivationDetail,
}

#[derive(Debug)]
pub struct ActivationDetail {
    pub activation_time: DateTime<FixedOffset>,
    pub duration: Duration,
}

3. 实现自定义反序列化逻辑

编写Serde Visitor解析Thhmmss-hhmm/PTmm格式的字符串,拆分时间和时长部分分别用Chrono解析:

use serde::de::{self, Deserializer};
use std::fmt;

fn deserialize_schedule<'de, D>(deserializer: D) -> Result<ActivationDetail, D::Error>
where
    D: Deserializer<'de>,
{
    struct ScheduleVisitor;

    impl<'de> de::Visitor<'de> for ScheduleVisitor {
        type Value = ActivationDetail;

        fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
            formatter.write_str("字符串格式需为 'Thhmmss-hhmm/PTmm'")
        }

        fn visit_str<E>(self, s: &str) -> Result<Self::Value, E>
        where
            E: de::Error,
        {
            // 按 '/' 分割时间部分和时长部分
            let parts: Vec<&str> = s.split('/').collect();
            if parts.len() != 2 {
                return Err(E::invalid_value(de::Unexpected::Str(s), &self));
            }

            // 解析带时区的激活时间(如 "T063000-0600")
            let activation_time = DateTime::parse_from_str(parts[0], "%T%z")
                .map_err(|_| E::custom(format!("无效的时间格式: {}", parts[0])))?;

            // 解析ISO 8601时长(如 "PT10M")
            let duration = Duration::parse(parts[1])
                .map_err(|_| E::custom(format!("无效的时长格式: {}", parts[1])))?;

            Ok(ActivationDetail { activation_time, duration })
        }
    }

    deserializer.deserialize_str(ScheduleVisitor)
}

4. 实现自定义序列化逻辑

把结构体数据格式化为指定的字符串格式:

use serde::ser::Serializer;

fn serialize_schedule<S>(detail: &ActivationDetail, serializer: S) -> Result<S::Ok, S::Error>
where
    S: Serializer,
{
    // 将时间格式化为 "Thhmmss-hhmm" 格式
    let time_str = detail.activation_time.format("%T%z").to_string();
    // Chrono的Duration转字符串默认就是ISO 8601格式
    let duration_str = detail.duration.to_string();
    // 拼接成目标格式
    let full_str = format!("{}/{}", time_str, duration_str);
    serializer.serialize_str(&full_str)
}

5. 在Actix-web中使用

直接将结构体作为请求体接收,或者作为响应返回:

use actix_web::{post, web, HttpResponse};

#[post("/activation")]
async fn set_activation(schedule: web::Json<ActivationRequest>) -> HttpResponse {
    println!("激活计划: {:?}", schedule.activation_schedule);
    HttpResponse::Ok().json(schedule)
}

#[actix_web::main]
async fn main() -> std::io::Result<()> {
    actix_web::HttpServer::new(|| {
        actix_web::App::new()
            .service(set_activation)
    })
    .bind(("127.0.0.1", 8080))?
    .run()
    .await
}

注意事项

  • 如果你的格式存在变体(比如时区为+hhmm、时长包含小时/秒等),Chrono的DateTime::parse_from_str和Duration::parse都能兼容标准ISO格式,无需额外修改
  • 若不需要外层ActivationRequest结构体,可以直接为ActivationDetail实现Serialize和Deserialize trait,替代使用serde属性的方式

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.11 04:33:23