Tim*_*sée 5 serialization yaml rust serde
我有一个项目映射,我想序列化为一个结构列表,每个结构都有一个对应键的字段。
想象一下,有一个这样的YAML文件:
name_a:
some_field: 0
name_b:
some_field: 0
name_c:
some_field: 0
Run Code Online (Sandbox Code Playgroud)
以及类似的结构:
struct Item {
name: String,
some_field: usize,
}
Run Code Online (Sandbox Code Playgroud)
我想将命名项反序列化为Vec<Item>而不是Map<String, Item>。项目名称(name_a,...)放在对象的name字段中Item。
我尝试了以下操作:
extern crate serde_yaml;
use std::fs::read_to_string;
let contents = read_to_string("file.yml").unwrap();
let items: Vec<Item> = serde_yaml::from_str(&contents).unwrap();
Run Code Online (Sandbox Code Playgroud)
但是,这不起作用并产生invalid type: map, expected a sequence错误。
我宁愿避免创建一个Map<String, PartialItem>转换为的瞬态,Vec也宁愿不实现其他PartialItem结构。可以使用Option<String>as name,尽管我认为这不是最佳选择。
一种方法是自己反序列化地图:
use std::fmt;
use serde::de::{Deserialize, Deserializer, MapAccess, Visitor};
use serde_derive::Deserialize;
struct ItemMapVisitor {}
impl ItemMapVisitor {
fn new() -> Self {
Self {}
}
}
#[derive(Debug, Deserialize)]
struct SomeField {
some_field: u32,
}
#[derive(Debug)]
struct Item {
name: String,
some_field: u32,
}
#[derive(Debug)]
struct VecItem(Vec<Item>);
impl Item {
fn new(name: String, some_field: u32) -> Self {
Self { name, some_field }
}
}
impl<'de> Visitor<'de> for ItemMapVisitor {
type Value = VecItem;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str("name: somefield:")
}
fn visit_map<M>(self, mut access: M) -> Result<Self::Value, M::Error>
where
M: MapAccess<'de>,
{
let mut items = Vec::with_capacity(access.size_hint().unwrap_or(0));
while let Some((key, value)) = access.next_entry::<String, SomeField>()? {
items.push(Item::new(key, value.some_field));
}
Ok(VecItem(items))
}
}
impl<'de> Deserialize<'de> for VecItem {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
deserializer.deserialize_map(ItemMapVisitor::new())
}
}
fn main() {
let contents = r#"
name_a:
some_field: 0
name_b:
some_field: 1
name_c:
some_field: 2
"#;
let items: VecItem = serde_yaml::from_str(&contents).unwrap();
println!("{:#?}", items);
}
Run Code Online (Sandbox Code Playgroud)
输出:
use std::fmt;
use serde::de::{Deserialize, Deserializer, MapAccess, Visitor};
use serde_derive::Deserialize;
struct ItemMapVisitor {}
impl ItemMapVisitor {
fn new() -> Self {
Self {}
}
}
#[derive(Debug, Deserialize)]
struct SomeField {
some_field: u32,
}
#[derive(Debug)]
struct Item {
name: String,
some_field: u32,
}
#[derive(Debug)]
struct VecItem(Vec<Item>);
impl Item {
fn new(name: String, some_field: u32) -> Self {
Self { name, some_field }
}
}
impl<'de> Visitor<'de> for ItemMapVisitor {
type Value = VecItem;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str("name: somefield:")
}
fn visit_map<M>(self, mut access: M) -> Result<Self::Value, M::Error>
where
M: MapAccess<'de>,
{
let mut items = Vec::with_capacity(access.size_hint().unwrap_or(0));
while let Some((key, value)) = access.next_entry::<String, SomeField>()? {
items.push(Item::new(key, value.some_field));
}
Ok(VecItem(items))
}
}
impl<'de> Deserialize<'de> for VecItem {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
deserializer.deserialize_map(ItemMapVisitor::new())
}
}
fn main() {
let contents = r#"
name_a:
some_field: 0
name_b:
some_field: 1
name_c:
some_field: 2
"#;
let items: VecItem = serde_yaml::from_str(&contents).unwrap();
println!("{:#?}", items);
}
Run Code Online (Sandbox Code Playgroud)
如果你不想要Somefield结构。你也可以使用这个:
#[derive(Debug, Deserialize)]
struct Item {
#[serde(skip)]
name: String,
some_field: u32,
}
while let Some((key, value)) = access.next_entry::<String, Item>()? {
items.push(Item::new(key, value.some_field));
}
Run Code Online (Sandbox Code Playgroud)
但这可能会添加一些无用的副本。
Item::name定义字段的默认值
#[derive(Debug, Serialize, Deserialize)]
struct Item {
#[serde(default)]
name: String,
some_field: usize,
}
Run Code Online (Sandbox Code Playgroud)
通过这个技巧,Item可以用于反序列化和转换为 a Vecof Items:
let contents = read_to_string("file.yml").unwrap();
let items: HashMap<String, Item> = serde_yaml::from_str(&contents).unwrap();
let slist: Vec<Item> = items
.into_iter()
.map(|(k, v)| Item {
name: k,
some_field: v.some_field,
})
.collect();
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
526 次 |
| 最近记录: |