You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

无法将JSON转换为Polars DataFrame:“从非数组JSON读取数组”问题排查

问题描述

从API获取并反序列化JSON后,无法转换为Polars DataFrame,报错read an Array from a non-Array JSON。

定义的结构体与函数

//  structs for option price query
#[derive(Serialize, Deserialize, Debug)]
pub struct OptionResponse {
    pub tables: Vec<OptionTable>,
}

#[derive(Serialize, Deserialize, Debug)]
pub struct OptionTable {
    pub table: OptionDataTable,
}

#[derive(Serialize, Deserialize, Debug)]
pub struct OptionDataTable {
    pub volume: Vec<f64>,
    pub open: Vec<f64>,
    pub close: Vec<f64>,
    pub high: Vec<f64>,
    pub low: Vec<f64>,
}

pub async fn ifind_option_read(option_code: &str, access_token: &str, start_time_str: &str, end_time_str: &str) -> Result<(), Box<dyn std::error::Error>> {
    let url = "https://quantapi.51ifind.com/api/v1/high_frequency";

    // create the high-frequency prices request
    let request_data = maplit::hashmap! {
        "codes" => option_code,
        "indicators" => "volume,open,close,high,low",
        "starttime" => start_time_str,
        "endtime" => end_time_str,
    };
    let request_json = serde_json::to_string(&request_data)?;

    // send the HTTP POST request
    let client = reqwest::Client::new();
    let response = client
        .post(url)
        .header(reqwest::header::CONTENT_TYPE, "application/json")
        .header("access_token", access_token)
        .body(request_json)
        .send()
        .await?;
    let response_json: OptionResponse = response.json().await?;
    let response_body = &response_json.tables[0].table;
    println!("{:?}", &response_body);
    // Convert response_json to byte array
    let json_bytes = serde_json::to_vec(&response_body)?;
    // read the response into df, per method of JsonReader
    let df = JsonReader::new(Cursor::new(json_bytes))
        .with_batch_size(1024)
        .infer_schema_len(None)
        .finish().unwrap();
    println!("{:?}", df);
    Ok(())
}

返回的response_body数据

OptionDataTable { volume: [0.0, 0.0, 0.0], open: [1263.0, 1263.0, 1263.0], close: [1263.0, 1263.0, 1263.0], high: [1263.0, 1263.0, 1263.0], low: [1263.0, 1263.0, 1263.0] }

报错信息:read an Array from a non-Array JSON


解决方案

原因分析

Polars的JsonReader默认期望行优先的JSON结构(即包含多个对象的数组,每个对象对应DataFrame的一行),但你的OptionDataTable序列化后是列优先的JSON对象:每个字段是一个数组,对应DataFrame的一列。这种结构不符合JsonReader的默认解析规则,因此报错。

方法1:直接从列向量构建DataFrame(推荐)

既然已经有反序列化后的列向量,无需再序列化回JSON再读取,直接用Polars的API构建:

修改代码中转换DataFrame的部分:

// 直接从列向量构建Series,再创建DataFrame
let df = DataFrame::new(vec![
    Series::new("volume", &response_body.volume),
    Series::new("open", &response_body.open),
    Series::new("close", &response_body.close),
    Series::new("high", &response_body.high),
    Series::new("low", &response_body.low),
])?;
println!("{:?}", df);

这种方法避免了额外的序列化/反序列化开销,效率更高,也不会有格式不匹配问题。

方法2:转换为行优先JSON后再读取(不推荐)

如果必须使用JsonReader,需要将列优先结构转换为行优先数组:

// 转换为行优先的Vec<HashMap>
let rows: Vec<HashMap<&str, f64>> = response_body.volume.iter()
    .zip(response_body.open.iter())
    .zip(response_body.close.iter())
    .zip(response_body.high.iter())
    .zip(response_body.low.iter())
    .map(|((((v, o), c), h), l)| {
        let mut map = HashMap::new();
        map.insert("volume", *v);
        map.insert("open", *o);
        map.insert("close", *c);
        map.insert("high", *h);
        map.insert("low", *l);
        map
    })
    .collect();

// 序列化为JSON数组,再用JsonReader读取
let json_bytes = serde_json::to_vec(&rows)?;
let df = JsonReader::new(Cursor::new(json_bytes))
    .with_batch_size(1024)
    .infer_schema_len(None)
    .finish()?;
println!("{:?}", df);

此方法多了一次数据转换,效率低于方法1,仅适合必须使用JsonReader的场景。


内容的提问来源于stack exchange,提问作者Arthur Zhang

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.17 00:57:53