#testing #rust #serde
Вопрос:
Макросы Serde derive имеют возможность управлять сериализацией/десериализацией поля с помощью атрибута #[serde(with = "module")]
поля. "module"
Должны быть функции сериализации и десериализации с правильными аргументами и типами возвращаемых значений.
Пример, который, к сожалению, стал слишком надуманным:
use serde::{Deserialize, Serialize};
#[derive(Debug, Default, PartialEq, Eq)]
pub struct StringPair(String, String);
mod stringpair_serde {
pub fn serialize<S>(sp: amp;super::StringPair, ser: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
ser.serialize_str(format!("{}:{}", sp.0, sp.1).as_str())
}
pub fn deserialize<'de, D>(d: D) -> Result<super::StringPair, D::Error>
where
D: serde::Deserializer<'de>,
{
d.deserialize_str(Visitor)
}
struct Visitor;
impl<'de> serde::de::Visitor<'de> for Visitor {
type Value = super::StringPair;
fn expecting(amp;self, f: amp;mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "a pair of strings separated by colon (:)")
}
fn visit_str<E>(self, s: amp;str) -> Result<Self::Value, E>
where
E: serde::de::Error,
{
Ok(s.split_once(":")
.map(|tup| super::StringPair(tup.0.to_string(), tup.1.to_string()))
.unwrap_or(Default::default()))
}
}
}
#[derive(Serialize, Deserialize)]
struct UsesStringPair {
// Other fields ...
#[serde(with = "stringpair_serde")]
pub stringpair: StringPair,
}
fn main() {
let usp = UsesStringPair {
stringpair: StringPair("foo".to_string(), "bar".to_string()),
};
assert_eq!(
serde_json::json!(amp;usp).to_string(),
r#"{"stringpair":"foo:bar"}"#
);
let usp: UsesStringPair = serde_json::from_str(r#"{"stringpair":"baz:qux"}"#).unwrap();
assert_eq!(
usp.stringpair,
StringPair("baz".to_string(), "qux".to_string())
)
}
Тестирование производной сериализации для UsesStringPair
тривиально с помощью простых утверждений. Но я посмотрел на serde_test
пример, поскольку для меня это тоже имеет смысл.
Тем не менее, я хочу иметь возможность самостоятельно тестировать stringpair_serde::{serialize, deserialize}
функции (например, если мой ящик предоставляет только mycrate::StringPair
и mycrate::stringpair_serde
, и UsesStringPair
предназначен для реализации пользователями ящика).
Один из способов , которым я занимался, — это создание serde_json::Serializer
(использование new
, требует io::Write
реализации, которую я не мог понять, как создавать и использовать тривиально, но это отдельный вопрос) и вызов сериализации с помощью созданного сериализатора, а затем утверждение результата, как и раньше. Однако это не проверяет ни одну/все реализации serde::Serializer
, только ту , которая указана в serde_json
.
Мне интересно, есть ли метод, подобный приведенному в serde_test
примере, который работает для функций ser/deser, предоставляемых модулем.
Комментарии:
1. Почему бы не вывести эти две черты непосредственно для
StringPair
себя?2. Что касается вашего другого (подразумеваемого) вопроса,
amp;mut Vec<u8>
io::Write
пожалуйста .3. @RuifengXie Несколько причин: могут быть разные способы сериализации в одно и то же время. Пример, который я могу привести, таков
chrono::DateTime<Tz>
. Он может быть сериализован как строка, как временная метка Unix, как целочисленная временная метка, но с более высоким разрешением (например, миллисекунды и т.д.) Таким образом, тип сам реализует функции сериализации и десериализации, но chrono также предоставляет подмодули , которые можно использоватьwith = ...
для сериализацииDateTime
значения различными способами.4. Также может быть так, что, поскольку ящик не предоставляет фактический тип, просто формат сериализации/десериализации для типа, определенного в другом месте (не может реализовывать признаки или выводить типы, определенные в другом месте). Другой, связанный, но другой сценарий: модуль записи предоставляет тип, например
StringPair
, и он реализует признаки ser/de, но модуль записи также хочет предоставить ser/de для монадических контейнеров этого типа, напримерOption<StringPair>
, сериализуется по-другомуStringPair
. Множество причин для необходимости автономного модуля ser/de вместо реализации признаков типа.