无法将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
相关产品推荐
相关产品推荐

