如何使用 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:
- 字段结果为
success
:T
- 字段结果为
error
:E
为了将这两个泛型类型分组,我使用了 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)
将被反序列化success
和 BaseResponseContent::Error(E)
将在 error
?
上反序列化
有一件事很清楚,为 BaseResponseContent 派生 Deserialize
是不够的,因为反序列化器将在 content
中寻找名为 Success
或 Error
的字段。
Error: unknown variant `username`, expected `Success` or `Error` at line 1 column 78
BaseResponseResult
和 BaseResponseContent
之间的语义重复使得这有点困难。如果可能,我会从 BaseResponse
中删除 result
字段。相反,BaseResponseContent
的变体包含相同的信息。
字段 result
和 content
然后匹配一个 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),
}
如果result
字段需要存在并且不能被其他方式替换,例如函数调用,我会先反序列化为上述结构,然后将其转换为最终结构。
我正在尝试反序列化以下结构 (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:
- 字段结果为
success
:T
- 字段结果为
error
:E
为了将这两个泛型类型分组,我使用了 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)
将被反序列化success
和 BaseResponseContent::Error(E)
将在 error
?
有一件事很清楚,为 BaseResponseContent 派生 Deserialize
是不够的,因为反序列化器将在 content
中寻找名为 Success
或 Error
的字段。
Error: unknown variant `username`, expected `Success` or `Error` at line 1 column 78
BaseResponseResult
和 BaseResponseContent
之间的语义重复使得这有点困难。如果可能,我会从 BaseResponse
中删除 result
字段。相反,BaseResponseContent
的变体包含相同的信息。
字段 result
和 content
然后匹配一个 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),
}
如果result
字段需要存在并且不能被其他方式替换,例如函数调用,我会先反序列化为上述结构,然后将其转换为最终结构。