Тестирование функций сериализации/десериализации для атрибута serde «с»

#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 вместо реализации признаков типа.