使用 serde::from_value 反序列化为泛型类型

Deserialize into a generic type with serde::from_value

提问人:pielgrzym 提问时间:9/1/2023 更新时间:9/1/2023 访问量:125

问:

我想将API json响应反序列化为使用泛型类型的结构。下面的代码无法编译,我无法弄清楚如何使其工作:

use serde::Deserialize;
use serde_json; // 1.0.102

#[derive(Deserialize)]
struct ApiResult<T> {
    pub data: T,
    pub status: String
}

fn get_result<T>(json: serde_json::Value) -> Result<ApiResult<T>, anyhow::Error> {
    let r: ApiResult<T> = serde_json::from_value(json)?;
    Ok(r)
}

fn main() -> Result<(),anyhow::Error> {
    let json = serde_json::json!({
        "data": 1,
        "status": "ok"
    });
    
    let r2: ApiResult<i64> = get_result::<i64>(json).unwrap();
    
    Ok(())
}

它会产生一个错误:

Compiling playground v0.0.1 (/playground)
error[E0277]: the trait bound `T: Deserialize<'_>` is not satisfied
   --> src/main.rs:11:27
    |
11  |     let r: ApiResult<T> = serde_json::from_value(json)?;
    |                           ^^^^^^^^^^^^^^^^^^^^^^ the trait `Deserialize<'_>` is not implemented for `T`
    |
note: required for `ApiResult<T>` to implement `for<'de> Deserialize<'de>`
   --> src/main.rs:4:10
    |
4   | #[derive(Deserialize)]
    |          ^^^^^^^^^^^ unsatisfied trait bound introduced in this `derive` macro
5   | struct ApiResult<T> {
    |        ^^^^^^^^^^^^
    = note: required for `ApiResult<T>` to implement `DeserializeOwned`
note: required by a bound in `from_value`
   --> /playground/.cargo/registry/src/index.crates.io-6f17d22bba15001f/serde_json-1.0.102/src/value/mod.rs:983:8
    |
981 | pub fn from_value<T>(value: Value) -> Result<T, Error>
    |        ---------- required by a bound in this function
982 | where
983 |     T: DeserializeOwned,
    |        ^^^^^^^^^^^^^^^^ required by this bound in `from_value`
    = note: this error originates in the derive macro `Deserialize` (in Nightly builds, run with -Z macro-backtrace for more info)
help: consider restricting type parameter `T`
    |
10  | fn get_result<T: _::_serde::Deserialize<'_>>(json: serde_json::Value) -> Result<ApiResult<T>, anyhow::Error> {
    |                ++++++++++++++++++++++++++++

For more information about this error, try `rustc --explain E0277`.
error: could not compile `playground` (bin "playground") due to previous error

链接到游乐场

rust 反序列化 serde serde-json

评论

0赞 user2722968 9/1/2023
目前还不清楚这里的问题是什么,因为错误消息从字面上说要做什么:需要实现才能实现任何实现。正如编译器所说,签名需要符合Tserde::DeserializeApiResult<T>Deserializeget_resultget_result<T: for<'a> serde::Deserialize<'a>>(json: serde_json::Value) -> Result<ApiResult<T>, anyhow::Error>
0赞 pielgrzym 9/1/2023
好点子 - 我不知道该怎么做,因为最后一行:)中令人困惑的例子

答:

4赞 Jmb 9/1/2023 #1

在函数中,需要指定类型是可反序列化的 ():get_resultTT: for<'a> Deserialize<'a>

use serde::Deserialize;
use serde_json; // 1.0.102

#[derive(Deserialize)]
struct ApiResult<T> {
    pub data: T,
    pub status: String
}

fn get_result<T: for<'a> Deserialize<'a>>(json: serde_json::Value) -> Result<ApiResult<T>, anyhow::Error> {
    let r: ApiResult<T> = serde_json::from_value(json)?;
    Ok(r)
}

fn main() -> Result<(),anyhow::Error> {
    let json = serde_json::json!({
        "data": 1,
        "status": "ok"
    });
    
    let r2: ApiResult<i64> = get_result::<i64>(json).unwrap();
    
    Ok(())
}

操场

评论

0赞 pielgrzym 9/1/2023
谢谢!!你能告诉我为什么会这样吗?我还没有见过语法 - 它是怎么称呼的?for
0赞 Jmb 9/1/2023
@pielgrzym 这种语法被称为“Higher-Rank Trait Bound”(或 HRTB)for)
0赞 Jmb 9/1/2023
但请注意,HRTB 不是这里的关键问题。主要问题是添加了隐式边界,因此 中需要相同的边界。#[derive(Deserialize)]T: Deserializeget_bound
0赞 Jmb 9/1/2023
由于具有生存期参数,因此您需要特定的生存期(例如)或HRTB(基本上意味着“任何生存期”)。DeserializeT: Deserialize<'static>
4赞 cafce25 9/1/2023
我认为在这种情况下,还有 DeserializeOwned,它本质上是 HRTB 作为一种特征。