Search code examples
rusterror-handlingserdeserde-json

How do you propagate errors in a Rust serde serializer?


I'm trying to implement serde's serialize_with attribute.

I have this code:

use serde::Serializer;

pub fn serialize_json_as_string<S>(json: &serde_json::value::Value, s: S) -> Result<S::Ok, S::Error>
where
    S: Serializer,
{
    let string = serde_json::to_string(json).unwrap();
    s.serialize_str(&string)
}

I don't like the unwrap() on the second to last line. But I can't figure out how to convert the error that to_string returns into S::Error.

I've tried replacing the let line with something like:

let string = serde_json::to_string(json)?;

And I got:

error[E0277]: `?` couldn't convert the error to `<S as serde::Serializer>::Error`
  --> src/model/field_type.rs:30:45
   |
26 | pub fn serialize_json_as_string<S>(json: &serde_json::value::Value, s: S) -> Result<S::Ok, S::Error>
   |                                                                              ----------------------- expected `<S as serde::Serializer>::Error` because of this
...
30 |     let string = serde_json::to_string(json)?;
   |                                             ^ the trait `From<serde_json::Error>` is not implemented for `<S as serde::Serializer>::Error`
   |
   = note: the question mark operation (`?`) implicitly performs a conversion on the error value using the `From` trait
   = note: required for `Result<<S as serde::Serializer>::Ok, <S as serde::Serializer>::Error>` to implement `FromResidual<Result<Infallible, serde_json::Error>>`
help: consider further restricting the associated type
   |
28 |     S: Serializer, <S as serde::Serializer>::Error: From<serde_json::Error>
   |                  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

For more information about this error, try `rustc --explain E0277`.

And also tried replacing the whole thing with:

match serde_json::to_string(json) {
   Ok(string) => s.serialize_str(&string),
   Err(e) => Err(e.into()),
}

Similar error:

error[E0277]: the trait bound `<S as serde::Serializer>::Error: From<serde_json::Error>` is not satisfied
  --> src/model/field_type.rs:32:25
   |
32 |         Err(e) => Err(e.into()),
   |                         ^^^^ the trait `From<serde_json::Error>` is not implemented for `<S as serde::Serializer>::Error`
   |
   = note: required for `serde_json::Error` to implement `Into<<S as serde::Serializer>::Error>`
help: consider further restricting the associated type
   |
28 |     S: Serializer, <S as serde::Serializer>::Error: From<serde_json::Error>
   |                  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

For more information about this error, try `rustc --explain E0277`.

Since I don't own Serde, I assume I can't implement into, so I need some kind of manual conversion. I couldn't figure out what that would look like.


Solution

  • The error is not a serde error, but you can wrap it in a serde custom error like so:

    use serde::{Serializer, ser::Error};
    
    pub fn serialize_json_as_string<S>(json: &serde_json::value::Value, s: S) -> Result<S::Ok, S::Error>
    where
        S: Serializer,
    {
        let string = serde_json::to_string(json).map_err(S::Error::custom)?;
        s.serialize_str(&string)
    }
    

    Note that you need to import serde::ser::Error as that's the trait where the custom function comes from.