jeanpaul62
jeanpaul62

Reputation: 10581

How do I modify the JSON output for a Result<T,E> serialized with serde?

A simple code:

use serde::Serialize;

#[derive(Serialize)]
struct MyStruct {
    foo: Result<u32, String>
}

fn main() {
    let m = MyStruct {
        foo: Ok(43)
    };
    let n = MyStruct {
        foo: Err("oh no!".into())
    };

    println!("{}", serde_json::to_string_pretty(&m).unwrap());
    println!("{}", serde_json::to_string_pretty(&n).unwrap());
}

This outputs (playground):

{
  "foo": {
    "Ok": 43
  }
}
{
  "foo": {
    "Err": "oh no!"
  }
}

Can I modify the serializer to have a custom output for Result<T,E>? I would like something like:

// No "Ok" field in case of Ok(T)
{
  "foo": 43
}
// Rename "Err" to "error" in case of Err(E)
{
  "foo": {
    "error": "oh no!"
  }
}

Upvotes: 0

Views: 959

Answers (1)

Peter Hall
Peter Hall

Reputation: 58835

Serde attributes are not powerful enough to do the transformation from the default Result serialization to what you want, so you will need to write custom serialization. Fortunately, it is quite simple:

use serde::{Serialize, Serializer, ser::SerializeMap};

struct MyStruct {
    foo: Result<u32, String>
}

impl Serialize for MyStruct {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: Serializer,
    {
        let mut map = serializer.serialize_map(Some(1))?;
        match &self.foo {
            Ok(value) => map.serialize_entry("foo", &value)?,
            Err(error) => map.serialize_entry("foo", &MyError { error } )?,
        }
        map.end()
    }
}

// This is just used internally to get the nested error field
#[derive(Serialize)]
struct MyError<E> {
    error: E,
}

Upvotes: 4

Related Questions