我有以下 json 文件,我想在 Rust 结构中反序列化:
{
"name": "Manuel",
"friends": {
"id1": 1703692376,
...
}
...
}
好友标签包含 ID 和时间戳 我创建了一个像这样的 Rust 结构:
use std::collections::HashMap;
use serde::{Deserialize, Serialize};
#[derive(Debug,Clone,Serialize,Deserialize)]
pub struct MyStruct{
name: String,
friends: HashMap<String, i64>
}
这很好用,但我实际上想要这样的东西:
use std::collections::HashMap;
use serde::{Deserialize, Serialize};
#[derive(Debug,Clone,Serialize,Deserialize)]
pub struct MyStruct{
name: String,
friends: HashMap<String, SystemTime>
}
所以我想将整数值转换为 SystemTime 类型。可以用 serde 做到这一点吗?
serde_with
已满足您的需求。它支持各种格式的 SystemTime
反/序列化,包括自纪元以来的秒数,并且与 #[serde(with)]
不同,它支持嵌套类型:
#[serde_with::serde_as]
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MyStruct {
name: String,
#[serde_as(as = "HashMap<_, serde_with::TimestampSeconds>")]
friends: HashMap<String, SystemTime>,
}
首先,让我们创建转换函数
unix_seconds_to_system
和 system_to_unix_seconds
在 i64
和 SystemTime
之间进行转换。
fn unix_seconds_to_system(unix_seconds: i64) -> SystemTime {
if unix_seconds.is_positive() {
let d = Duration::from_secs(unix_seconds as u64);
SystemTime::UNIX_EPOCH + d
} else {
let d = Duration::from_secs(-unix_seconds as u64);
SystemTime::UNIX_EPOCH - d
}
}
fn system_to_unix_seconds(system: SystemTime) -> i64 {
match system.duration_since(SystemTime::UNIX_EPOCH) {
Ok(t) => t.as_secs() as i64,
Err(e) => -(e.duration().as_secs() as i64),
}
}
#[serde(with)]
指定用于反序列化和序列化的自定义函数,而不是使用 Deserialize
的 Serialize
和 HashMap<String, SystemTime>
实现。
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MyStruct {
name: String,
#[serde(with = "time_map")]
friends: HashMap<String, SystemTime>,
}
mod time_map {
use super::*;
use serde::{Deserializer, Serializer};
pub fn serialize<S>(map: &HashMap<String, SystemTime>, ser: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
todo!()
}
pub fn deserialize<'de, D>(de: D) -> Result<HashMap<String, SystemTime>, D::Error>
where
D: Deserializer<'de>,
{
todo!()
}
}
有两种方法可以解决这个问题。更简单的解决方案是经过
HashMap<String, i64>
并转换。
pub fn serialize<S>(map: &HashMap<String, SystemTime>, ser: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
map.iter()
.map(|(k, &v)| (k, system_to_unix_seconds(v)))
.collect::<HashMap<_, _>>()
// This serializes `HashMap<&String, i64>`, which has the same
// representation as `HashMap<String, i64>`.
.serialize(ser)
}
pub fn deserialize<'de, D>(de: D) -> Result<HashMap<String, SystemTime>, D::Error>
where
D: Deserializer<'de>,
{
Ok(HashMap::<String, i64>::deserialize(de)?
.into_iter()
.map(|(k, v)| (k, unix_seconds_to_system(v)))
.collect())
}
缺点是,反序列化时分配两个
HashMap
,序列化时分配一个,而不是分别分配 1 和 0。为了避免这种情况,您可以直接使用 Serializer
和 Deserializer
方法创建完全自定义的函数。
use serde::de::Visitor;
use serde::ser::SerializeMap;
pub fn serialize<S>(map: &HashMap<String, SystemTime>, ser: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let mut ser_map = ser.serialize_map(Some(map.len()))?;
for (k, &v) in map {
let unix_seconds = system_to_unix_seconds(v);
ser_map.serialize_entry(k, &unix_seconds)?;
}
ser_map.end()
}
pub fn deserialize<'de, D>(de: D) -> Result<HashMap<String, SystemTime>, D::Error>
where
D: Deserializer<'de>,
{
struct VisitTimeMap;
impl<'de> Visitor<'de> for VisitTimeMap {
type Value = HashMap<String, SystemTime>;
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(formatter, "a map of strings to unix timestamps")
}
fn visit_map<A>(self, mut de_map: A) -> Result<Self::Value, A::Error>
where
A: serde::de::MapAccess<'de>,
{
let mut map = HashMap::with_capacity(de_map.size_hint().unwrap_or_default());
while let Some((k, v)) = de_map.next_entry::<String, i64>()? {
let system_time = unix_seconds_to_system(v);
map.insert(k, system_time);
}
Ok(map)
}
}
de.deserialize_map(VisitTimeMap)
}