如何使用 serde 反序列化具有泛型类型的枚举

How deserialize an enum with generic types using serde

我正在尝试反序列化以下结构 (BaseResponse<T, E>):

#[derive(Copy, Clone, Deserialize, Eq, PartialEq)]
pub struct BaseResponse<T, E> {
    result: BaseResponseResult,
    error_code: u32,
    error_message: String,
    content: BaseResponseContent<T, E>,
}

#[derive(Copy, Clone, Deserialize, Eq, PartialEq)]
#[serde(rename_all = "snake_case")]
pub enum BaseResponseResult {
    Success,
    Error,
}

#[derive(Copy, Clone, Deserialize, Eq, PartialEq)]
pub enum BaseResponseContent<T, E> {
    Success(T),
    Error(E),
}

上述结构有一个类型 T 和一个类型 E 这些类型在字段 content 中找到 when:

为了将这两个泛型类型分组,我使用了 BaseResponseContent 枚举。

例如,让我们考虑以下响应内容类型。


#[derive(Copy, Clone, Deserialize, Eq, PartialEq)]
pub struct User {
    username: String,
    location: String,
    age: u32,
}

#[derive(Copy, Clone, Deserialize, Eq, PartialEq)]
pub struct FailedLookup {
    ip: String,
    time: String,
}

如果请求成功,API 将返回一个结果 success 和一个 User 对象:

{
    "result": "success",
    "error_code": 0,
    "error_message": "",
    "content": {
        "username": "someone",
        "location": "somewhere",
        "age": 20
    }
}

如果请求失败,API 将返回一个结果 error 和一个 FailedLookup 对象:

{
    "result": "error",
    "error_code": 404,
    "error_message": "no such user",
    "content": {
        "ip": "1.1.1.1",
        "time": "1624352175"
    }
}

我如何为 BaseResponseContent 实现反序列化或在 BaseResponse 中的 content 字段上使用 deserialize_with 以便类型 BaseResponseContent::Success(T) 将被反序列化successBaseResponseContent::Error(E) 将在 error?

上反序列化

有一件事很清楚,为 BaseResponseContent 派生 Deserialize 是不够的,因为反序列化器将在 content 中寻找名为 SuccessError 的字段。

Error: unknown variant `username`, expected `Success` or `Error` at line 1 column 78

Here you have a full demo

BaseResponseResultBaseResponseContent 之间的语义重复使得这有点困难。如果可能,我会从 BaseResponse 中删除 result 字段。相反,BaseResponseContent 的变体包含相同的信息。

字段 resultcontent 然后匹配一个 adjacently tagged enum. flatten 将字段内联到 BaseResponse.

#[derive(Clone, Deserialize, Eq, PartialEq)]
pub struct BaseResponse<T, E> {
    error_code: u32,
    error_message: String,
    #[serde(flatten)]
    content: BaseResponseContent<T, E>,
}

#[derive(Debug, Clone, Deserialize, Eq, PartialEq)]
#[serde(rename_all = "snake_case", tag = "result", content = "content")]
pub enum BaseResponseContent<T, E> {
    Success(T),
    Error(E),
}

Full example

如果result字段需要存在并且不能被其他方式替换,例如函数调用,我会先反序列化为上述结构,然后将其转换为最终结构。