Search code examples
httprustserde

Rust/Serde/HTTP: Serialize `Option<http::Method>`


I am trying to set up serde with my Request struct to turn it into JSON to pass it to an api:

#[serde_with::skip_serializing_none]
#[derive(Serialize, Debug, Default)]
#[serde(rename_all = "camelCase")]
pub struct Request {
    #[serde(with = "http_serde::uri")]
    url: http::Uri,
    http_response_body: Option<bool>,
    #[serde(with = "http_serde::method")]
    http_request_method: Option<http::Method>,
}

I would like to have some fields as optional in the sense that if they are None then I just want to omit them from serialization. I am using serde_with to skip None fields, and it works for http_response_body: Option<bool> but not for http_request_method: Option<http::Method>. I am getting the following error:

error[E0308]: mismatched types
   --> src\zyte.rs:7:10
    |
7   | #[derive(Serialize, Debug, Default)]
    |          ^^^^^^^^^
    |          |
    |          expected struct `Method`, found enum `Option`
    |          arguments to this function are incorrect
    |
    = note: expected reference `&Method`
               found reference `&'__a std::option::Option<Method>`
note: function defined here
   --> C:\Users\isaac\.cargo\registry\src\github.com-1ecc6299db9ec823\http-serde-1.1.2\src\lib.rs:237:12
    |
237 |     pub fn serialize<S: Serializer>(method: &Method, ser: S) -> Result<S::Ok, S::Error> {
    |            ^^^^^^^^^
    = note: this error originates in the derive macro `Serialize` (in Nightly builds, run with -Z macro-backtrace for more info)

For more information about this error, try `rustc --explain E0308`.
error: could not compile `rust-simple-scraper` due to previous error

What can I do to make this work?


Solution

  • You can wrap the crate's method::serialize with your own function that takes &Option<Method>.

    fn method_opt<S: Serializer>(method_opt: &Option<Method>, ser: S) -> Result<S::Ok, S::Error> {
        match method_opt {
            Some(method) => http_serde::method::serialize(method, ser),
            // This won't be encountered when using skip_serializing_none
            None => panic!("This should only be used with `serde_with::skip_serializing_none`"),
        }
    }
    

    Then use that for serializing.

    #[serde(serialize_with = "method_opt")]
    http_request_method: Option<http::Method>,
    

    If you don't use skip_serializing_none, you can change the None branch to use ser.serialize_none(), but this will only work with simple formats like JSON. Formats that have different handling of serialize_some like RON will end up inconsistent.

    If you want to create both serialization and deserialization for this struct, you can create two functions in a module as described in the serde documentation (example). This would have the same structure as the http_serde::method module.