我想解析.yaml
-文件的以下部分:
networks:
foo1: 192.168.1.0/24
bar1: 192.168.2.0/24
foo2: 2001:CAFE:1::/64
bar2: 2001:CAFE:2::/64
…其中每个属性的键是网络名称的标签,值表示分配给它的子网。反序列化结构应该类似于如下:
#[derive(Debug, PartialEq, Serialize, Deserialize)]
struct Networks {
networks: Vec<Network>
}
#[derive(Debug, PartialEq, Serialize, Deserialize)]
struct Network {
name: String,
subnet: String,
}
我宁愿这个符号
networks:
- name: foo1
subnet: 192.168.1.0/24
- ...
,因为这会增加不必要的样板文件。问题是我不能正确地将这个网络列表解析为结构,因为据我目前所知,结构的属性必须具有与之等效的键-并且由于这里的键名是"随机的";我不能那样做。
我发现的唯一其他解决方案是将条目解析为hashmap并自动将其转换为(String, String)的元组(然后网络将是一个Vec<(String, String)>)通过serde-tuple-vec-map crate(失去命名参数)。
这看起来很容易配置,但我还没有在其他地方找到任何解决方案。
实现自定义反序列化器并将其与#[serde(deserialize_with = "network_tuples")] networks: Vec<Network>
一起使用是一种方法:
fn network_tuples<'de, D>(des: D) -> Result<Vec<Network>, D::Error>
where
D: serde::Deserializer<'de>,
{
struct Vis(Vec<Network>);
impl<'de> serde::de::Visitor<'de> for Vis {
type Value = Vec<Network>;
fn expecting(&self, _formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
todo!("return nice descriptive error")
}
fn visit_map<A>(mut self, mut map: A) -> Result<Self::Value, A::Error>
where
A: serde::de::MapAccess<'de>,
{
while let Some((name, subnet)) = map.next_entry()? {
self.0.push(Network { name, subnet });
}
Ok(self.0)
}
}
des.deserialize_map(Vis(vec![]))
}
您可以通过首先反序列化到HashMap<String, String>
然后转换而不使用访问者,但这有性能损失,并且您将失去具有相同名称的两个网络的能力(不确定您是否想要这样做)。
游乐场
当然,你也可以结合我最喜欢serde技巧#[serde(from = "SomeStructWithVecStringString")]
serde-tuple-vec-map
箱。但是我已经用from
写了很多答案,所以我将参考一个例子。