rust: получить данные json с сайта

Рейтинг: 0Ответов: 1Опубликовано: 09.04.2023

Условный сайт site.com отдаёт данные в json, например, такие:

{
"a": {
    "text": ["abc", "xyz", "rty", "io", "f"], 
    "data": [
            [null, "txt", 18.1234, 12345678912345, -0.3]
            ]
     },
"b": {
    "text": ["qwe", "rty", "asd"], 
    "data": [
            [11343, 0, 20230407131355]
            ]
     }
}

хочу их получить, чтобы потом сохранить в базе postgresql одной записью в формате json. Пробую http-client reqwest и по ходу потребовался serde:

[dependencies]
reqwest = { version = "0.11", features = ["json"] }
tokio = { version = "1", features = ["full"] }
serde = { version = "1.0.159", features = ["derive"] }
serde_json = "1.0"

main.rs:

use serde::{Serialize, Deserialize};

#[derive(Serialize, Deserialize, Debug)]
struct Msg {
    a: A,
    b: B,
}

#[derive(Serialize, Deserialize, Debug)]
struct A {
    text: Vec<String>,
    data: Vec<Vec<Option<Datum>>>,
}

#[derive(Serialize, Deserialize, Debug)]
struct B {
    text: Vec<String>,
    data: Vec<Vec<i64>>,
}

#[derive(Serialize, Deserialize, Debug)]
#[serde(untagged)]
enum Datum {
    Double(f64),
    String(String),
}

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
      // ...
      let resp = reqwest::get("https://site.com")
                 .await?.json::<Msg>().await?;
      // ...
      println!("{:?}", resp);   

Ok(())
}

println! выводит не то, как ожидаю, а типа такого:

Msg { a: A { text: ["abc", "xyz", ...

             data: [[None, Some(String("txt")), Some(Double(18.1234), Some(Double(12345678912345.0)), Some(Double(-0.3))]]

надо так:

Msg { "a": { "text": ["abc", "xyz", ...
             "data": [[null, "txt", 18.1234, 12345678912345, -0.3]]  

а лучше без Msg, чтоб как оригинал:

{
"a": {
    "text": ["abc", "xyz", "rty", "io", "f"], 
    "data": [
            [null, "txt", 18.1234, 12345678912345, -0.3]
            ]
     },
"b": {
    "text": ["qwe", "rty", "asd"], 
    "data": [
            [11343, 0, 20230407131355]
            ]
     }
}

Как получить правильно, чтобы a и text в кавычках, без A без Some(String ... Double и целыми числами?

Ответы

▲ 2Принят

Как ни странно, json::<Msg>().await означает: "Десериализуй из JSON в объект типа Msg". То есть JSON у вас на руках не оказывается, а вместо него - сразу настоящая rust-структура (имплементация трейта Debug {:?} которой не претендует на совместимость с JSON). Потому тут есть два путя:

  1. Не десериализовывать тело, а вытащить его как текст, получив всамделешний JSON в виде строки:
let resp = reqwest::get("https://site.com").await?
                      .text().await?;

Потом, когда это будет необходимо, строку можно десериализовать в объект (let msg = serde_json::from_str::<Msg>(&resp)?;)

  1. Вручную сериализовать Msg обратно в JSON там, где вам это необходимо:
println!("{}", &serde_json::to_string(&resp)?);