代码之家  ›  专栏  ›  技术社区  ›  ams_132

为什么这个youtube XML数据中的这些字段没有使用Rust中的quick XML正确反序列化

  •  2
  • ams_132  · 技术社区  · 5 月前

    我正试图从端点反序列化youtube数据 http://www.youtube.com/feeds/videos.xml?channel_id=UCHnyfMqiRRG1u-2MsSQLbXA 进入 Feed 结构如下 Rust 程序。它将返回无 media_group 结构体中的字段 Entry ,即使原始XML包含信息。

    注意::粘贴 http://www.youtube.com/feeds/videos.xml?channel_id=UCHnyfMqiRRG1u-2MsSQLbXA 在浏览器中更好地可视化XML

    // youtube_data.rs
    
    use hyper::{Body, Client, Uri};
    use hyper_tls::HttpsConnector;
    use serde::Deserialize;
    use quick_xml::de::{from_str, DeError};
    use crate::internals::singularity::WidgetError;
    
    
    
    impl From<DeError> for WidgetError {
        fn from(err: DeError) -> Self {
            WidgetError::XmlParse(err.to_string())
        }
    }
    
    
    
    #[derive(Debug, Deserialize)]
    #[serde(rename = "feed")]
    pub struct Feed {
        id: String,
        #[serde(rename = "channelId", default)]
        yt_channel_id: Option<String>,
        title: String,
        published: String,
        #[serde(default)]
        #[serde(rename = "entry")]
        entries: Vec<Entry>,
    }
    
    #[derive(Debug, Deserialize)]
    struct Entry {
        id: String,
        title: String,
        published: String,
        #[serde(rename = "mediaGroup")]
        media_group: Option<MediaGroup>,
    }
    
    #[derive(Debug, Deserialize)]
    struct MediaGroup {
        #[serde(rename = "mediaTitle")]
        title: Option<String>,
        #[serde(rename = "mediaContent")]
        content: Option<MediaContent>,
        #[serde(rename = "mediaThumbnail")]
        thumbnail: Option<MediaThumbnail>,
        #[serde(rename = "mediaCommunity")]
        community: Option<MediaCommunity>,
    }
    
    #[derive(Debug, Deserialize)]
    struct MediaContent {
        #[serde(rename = "@url")]
        url: String,
        #[serde(rename = "@width")]
        width: u32,
        #[serde(rename = "@height")]
        height: u32,
    }
    
    #[derive(Debug, Deserialize)]
    struct MediaThumbnail {
        #[serde(rename = "@url")]
        url: String,
        #[serde(rename = "@width")]
        width: u32,
        #[serde(rename = "@height")]
        height: u32,
    }
    
    #[derive(Debug, Deserialize)]
    struct MediaCommunity {
        #[serde(rename = "mediaStarRating")]
        star_rating: Option<StarRating>,
        #[serde(rename = "mediaStatistics")]
        statistics: Option<Statistics>,
    }
    
    #[derive(Debug, Deserialize)]
    struct StarRating {
        #[serde(rename = "@count")]
        count: u64,
    }
    
    #[derive(Debug, Deserialize)]
    struct Statistics {
        #[serde(rename = "@views")]
        views: u64,
    }
    
    pub async fn get_youtube_vids_for_a_channel(channel_id: String) -> Result<Feed, WidgetError> {
        let url = format!(
            "https://www.youtube.com/feeds/videos.xml?channel_id={}",
            channel_id
        );
    
        let uri: Uri = url.parse().unwrap();
    
        let https = HttpsConnector::new();
        let client = Client::builder().build::<_, Body>(https);
        let res = client.get(uri).await?;
        let body = hyper::body::to_bytes(res.into_body()).await?;
        let response_text = String::from_utf8(body.to_vec())
            .map_err(|e| WidgetError::Utf8Error(e.to_string()))?; 
    
        // println!("{:#?}", response_text);
        let feed: Feed = from_str(&response_text)?;
    
        Ok(feed)
    }
    
    // singularity.rs
    
    #[derive(Debug, Error)]
    pub enum WidgetError {
        #[error("Hyper error: {0}")]
        Hyper(#[from] hyper::Error),
        #[error("No geocoding data found")]
        NoGeocodingData,
        #[error("Error in reading HTML file")]
        NoHtmlToString,
        #[error("Serde JSON error: {0}")]
        SerdeJson(#[from] serde_json::Error),
        #[error("No timezone found")]
        NoTimeZone,
        #[error("XML parsing error: {0}")]
        XmlParse(String),
        #[error("UTF-8 conversion error: {0}")]
        Utf8Error(String),
    }
    

    使用的依赖关系是

    [dependencies]
    hyper = { version = "0.14", features = ["full"] }
    hyper-tls = "0.5"
    regex = "1.10.5"
    serde = { version = "1.0", features = ["derive"] }
    thiserror = "1.0.63"
    quick-xml = { version = "0.37", features = ["serialize"] }
    

    即使原始XML包含数据,为什么它没有正确序列化它??

    1 回复  |  直到 5 月前
        1
  •  0
  •   Joseph Sible-Reinstate Monica    5 月前

    命名空间类似 media: serde目前不支持: https://github.com/tafia/quick-xml/issues/218

    作为一种解决方法,由于没有命名空间的同名字段,您可以直接删除前缀,例如重命名为 group :

    #[derive(Debug, Deserialize)]
    struct Entry {
        id: String,
        title: String,
        published: String,
        #[serde(rename = "group")]
        media_group: Option<MediaGroup>,
    }
    
    #[derive(Debug, Deserialize)]
    struct MediaGroup {
        #[serde(rename = "title")]
        title: Option<String>,
        #[serde(rename = "content")]
        content: Option<MediaContent>,
        #[serde(rename = "thumbnail")]
        thumbnail: Option<MediaThumbnail>,
        #[serde(rename = "community")]
        community: Option<MediaCommunity>,
    }
    
    #[derive(Debug, Deserialize)]
    struct MediaCommunity {
        #[serde(rename = "starRating")]
        star_rating: Option<StarRating>,
        #[serde(rename = "statistics")]
        statistics: Option<Statistics>,
    }