如何修改使用 serde 序列化的 Result<T,E> 的 JSON 输出?

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

一个简单的代码:

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());
}

这输出 (playground):

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

我可以修改序列化程序以具有 Result<T,E> 的自定义输出吗?我想要这样的东西:

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

Serde attributes 不够强大,无法将默认 Result 序列化转换为您想要的序列化,因此您需要编写自定义序列化。幸运的是,这很简单:

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,
}