Search code examples
rustdeserializationjson-deserializationserdereqwest

Rust - deserialization using serde/reqwest "Invalid type"


I'm trying to deserialize the following API response (for simplicity, I'll only copy two slices of the array, but it will be bigger in reality). The code is oversimplified to demonstrate the example.

API response:

[[1609632000000,32185,32968,34873,31975,18908.90248876],[1609545600000,29349.83250154,32183,33292,29000,22012.92431526]]

So it's a big array/vector, composed of arrays/vectors with six integer OR floats (their position will also vary).

For this, I'm trying to use the generics , but it seems I'm missing something since I cannot get it to compile..

it is failing with

thread 'main' panicked at 'called `Result::unwrap()` on an `Err` value: reqwest::Error { kind: Decode, source: Error("invalid type: integer `1609632000000`, expected struct T", line: 1, column: 15) }'

use blocking::Response;
use serde::{Deserialize, Serialize};
use reqwest::{blocking, Error, StatusCode};

struct allData<T> {
    data: Slice<T>
}

#[derive(Debug, Serialize, Deserialize)]
struct Slice<T>{
    data1: T,
    data2: T,
    data3: T,
    data4: T,
    data5: T,
    data6: T,

}


fn get_data() -> Option<Slice> /*I know the signature is not correct, read until the end for the correct one*/ {
    let url = format!("{}", my_url_string);
    let response: Slice<T> = blocking::get(&url).unwrap().json().unwrap();
    Some(response);


}

fn main() {
   let call_the_api = get_data();
   println!("{:?}", call_the_api);
}

What would be the correct way to use a Struct with generics that can return a vector of "Slice".

ie.

Vector{
    Slice {
     data1,
     data2,
     data3,
     data4,
     data5,
     data6,
},
    Slice {
      data1, 
      data2,
      data3,
      data4,
      data5,
      data6,
}
}

Solution

  • The derivation of Deserialize on your Slice struct does not work on a JSON array, instead it expects a JSON dict with fields data1, data2 and so on. Presumably, you don't want to manually implement the Deserialize trait for your type, so you'll want a Vec<Vec<T>> to model your nested arrays:

    #[derive(Deserialize)]
    #[serde(transparent)]
    struct AllData<T>(Vec<Vec<T>>);
    

    This type restricts all items in the Vec to be of type T. This means, you don't get to use some f64 and some i64, it'll either be all floats or all ints. To make this wrapper generic over both floats and ints, you'll probably want some enum:

    #[derive(Deserialize)]
    // note, this causes deserialization to try the variants top-to-bottom
    #[serde(untagged)]
    enum FloatOrInt {
        Int(i64),
        Float(f64),
    }
    

    With the enum, the type parameter T on AllData is not needed anymore, you can go for:

    #[derive(Deserialize)]
    #[serde(transparent)]
    struct AllData(Vec<Vec<FloatOrInt>>);
    

    If you are sure that the inner array has always length 6, you can replace it with an array: [FloatOrInt; 6].

    Putting it together, you'd get something like this:

    https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=52bd3ce7779cc9b7b152c681de8681c4