#xml #parser #derive #macro-derive #struct #automate #particularly

deserialize_xml

简化从 XML 解析结构体的过程,特别是通过 derive 宏来自动化实现

2 个不稳定版本

0.2.1 2022 年 11 月 19 日
0.2.0 2022 年 11 月 18 日
0.1.0 2022 年 9 月 22 日

#2970解析器实现

GPL-3.0-or-later

18KB
103 代码行

此包提供从 XML 反序列化结构体的工具;最值得注意的是,它提供了一个 [derive 宏][derive@DeserializeXml],用于自动化此过程(通过为您实现 DeserializeXml)。

注意:实现非常有限且不够优雅。我写这个纯粹是为了帮助推动我正在作为一个个人项目开发的一个新闻阅读器;不要期待“生产就绪”的任何东西...(见下面的注意事项。)

示例

基本

以下是您如何使用此包轻松解析一个非常简单的 XML 结构的示例

use deserialize_xml::DeserializeXml;

#[derive(Default, Debug, DeserializeXml)]
struct StringOnly {
title: String,
author: String,
}

let input = "<stringonly><title>Title</title><author>Author</author></stringonly>";
// `from_str` here was provided by `#[derive(DeserializeXml)]` above
let result = StringOnly::from_str(input).unwrap();
assert_eq!(result.title, "Title");
assert_eq!(result.author, "Author");

高级

此示例展示了更高级的功能

use deserialize_xml::DeserializeXml;

#[derive(Default, Debug, DeserializeXml)]
// This attribute indicates we should parse this struct upon encountering an <item> tag
#[deserialize_xml(tag = "item")]
struct StringOnly {
title: String,
author: String,
}

#[derive(Default, Debug, DeserializeXml)]
struct Channel {
title: String,
// This allows us to use an idiomatic name for the
// struct member instead of the raw tag name
#[deserialize_xml(tag = "lastUpdated")]
last_updated: String,
ttl: u32,
// (unfortunately, we need to repeat `tag = "item"` here for now)
#[deserialize_xml(tag = "item")]
entries: Vec<StringOnly>,
}

let input = r#"<channel>
<title>test channel please ignore</title>
<lastUpdated>2022-09-22</lastUpdated>
<ttl>3600</ttl>
<item><title>Article 1</title><author>Guy</author></item>
<item><title>Article 2</title><author>Dudette</author></item>
</channel>"#;

let result = Channel::from_str(input).unwrap();
assert_eq!(result.title, "test channel please ignore");
assert_eq!(result.last_updated, "2022-09-22");
assert_eq!(result.ttl, 3600);
assert_eq!(result.entries.len(), 2);
assert_eq!(result.entries[0].title, "Article 1");
assert_eq!(result.entries[0].author, "Guy");
assert_eq!(result.entries[1].title, "Article 2");
assert_eq!(result.entries[1].author, "Dudette");

注意事项

  • 目前对 Vec<T>/Option<T> 的支持非常有限。具体来说,宏执行一个 文本 检查以确定成员类型是否为,例如,Vec<T>;如果是这样,它创建一个空的 vec 并在遇到匹配的标签时将 DeserializeXml::from_reader 的结果(T)推入。注意强调 文本 检查:如果您“拼写” Vec<T> 的方式不同(例如,通过别名),或者使用自己的容器类型,则宏将失败。(对于 Option<T> 同样适用。)

  • 该宏仅支持结构体。

  • 为字符串和数值类型(例如 u8i8 等)提供了 DeserializeXml 的实现。若要添加对自定义类型的支持,请参阅本节

  • 类型为 Option<T> 的结构体字段,其中 T 也是一个结构体且已应用了 #[derive(DeserializeXml)] 属性,在解析过程中似乎会被跳过,除非正确设置了 tag 属性。这种情况也可能出现在其他边缘情况中,但这个例子具有教育意义。以下是一个简单的示例来说明这个问题。

use deserialize_xml::DeserializeXml;

#[derive(Default, Debug, DeserializeXml)]
struct Post {
title: String,
// The inner type has a weird name, but the generated parser uses the field name
// by default, so it will look for <attachment> tags--all good, or so you think...
attachment: Option<WeirdName>,
};

#[derive(Default, Debug, DeserializeXml)]
#[deserialize_xml(tag = "attachment")] // (*) - necessary!
struct WeirdName {
path: String,
mime_type: String,
}

let input = r#"<post>
<title>A Modest Proposal</title>
<attachment>
<path>./proposal_banner.jpg</path>
<mime_type>image/jpeg</mime_type>
</attachment>
</post>"#;

// So far, this looks like a very standard example...
let result = Post::from_str(input).unwrap();
assert_eq!(result.title, "A Modest Proposal");
// ..but without the line marked (*) above, result.attachment is None!
let attachment = result.attachment.unwrap();
assert_eq!(attachment.path, "./proposal_banner.jpg");
assert_eq!(attachment.mime_type, "image/jpeg");

没有行 (*),出了什么问题? Post::from_reader(由 Post::from_str 调用)将查找 <attachment> 标签,并尽职尽责地调用 WeirdName::from_reader 当它看到时。然而,WeirdName::from_reader 没有意识到有人将其称为 attachment,因此该实现的主体假设它应该只解析 <weirdname> 标签。由于找不到任何,我们不会解析我们的 <attachment>。通过将 #[deserialize_xml)] 属性添加到 WeirdName,我们确保 WeirdName::from_reader 的实现查找的是 <attachment> 标签,而不是 <weirdname> 标签。不幸的是,目前还没有方便的方法将 WeirdName 与多个标签关联。

为您的结构体实现 DeserializeXml

当然,您可以从头开始实现 DeserializeXml,但这样做通常涉及频繁重复一些样板 XML 解析器操作代码。相反,请参阅 impl_deserialize_xml_helper 的文档和实现,以更方便地处理常见情况。

依赖项

~1.5MB
~40K SLoC