如何将Polars DataFrame的AnyValue类型行/HashMap序列化为JSON?
问题:Polars DataFrame行转JSON时Datetime序列化失败
我通过迭代器读取Parquet文件生成Polars DataFrame的一行,构建代表该行的HashMap后,尝试转换为JSON时遇到了序列化错误。
我的Rust代码如下:
use polars::prelude::*; use std::iter::zip; use std::{fs::File, collections::HashMap}; fn main() -> anyhow::Result<()> { let file = File::open("0.parquet").unwrap(); let mut df = ParquetReader::new(file).finish()?; dbg!(df.schema()); let fields = df.fields(); let columns: Vec<&String> = fields.iter().map(|x| x.name()).collect(); df.as_single_chunk_par(); let mut iters = df.iter().map(|s| s.iter()).collect::<Vec<_>>(); for _ in 0..df.height() { let mut row = HashMap::new(); for (column, iter) in zip(&columns, &mut iters) { let value = iter.next().expect("should have as many iterations as rows"); row.insert(column, value); } dbg!(&row); let json = serde_json::to_string(&row).unwrap(); dbg!(json); break; } Ok(()) }
启用的特性标志:["parquet", "serde", "dtype-u8", "dtype-i8", "dtype-date", "dtype-datetime"]
执行serde_json::to_string(&row).unwrap()时触发错误:
thread 'main' panicked at 'called `Result::unwrap()` on an `Err` value: Error("the enum variant AnyValue::Datetime cannot be serialized", line: 0, column: 0)', src/main.rs:47:48
另外,由于Rust的孤儿规则,我无法自行实现AnyValue::Datetime的序列化。请问将该行序列化为JSON的最佳方法是什么?
解决方案
方法1:利用Polars内置的JSON序列化能力
Polars本身支持将DataFrame或行直接序列化为JSON,不需要手动构建HashMap,能自动处理所有数据类型的序列化:
use polars::prelude::*; use std::fs::File; fn main() -> anyhow::Result<()> { let file = File::open("0.parquet").unwrap(); let mut df = ParquetReader::new(file).finish()?; df.as_single_chunk_par(); // 取第一行并转为JSON let first_row = df.slice(0, 1); let json_str = first_row.write_json(JsonWriterOptions::default())?; dbg!(json_str); Ok(()) }
方法2:手动转换AnyValue为可序列化类型
如果必须保留手动构建HashMap的逻辑,可以将AnyValue转换为serde_json::Value类型,手动处理每种数据类型的序列化:
use polars::prelude::*; use std::iter::zip; use std::{fs::File, collections::HashMap}; use serde_json::Value; fn any_value_to_json_value(val: AnyValue<'_>) -> Value { match val { AnyValue::Null => Value::Null, AnyValue::Boolean(b) => Value::Bool(b), AnyValue::UInt8(n) => Value::Number(n.into()), AnyValue::Int8(n) => Value::Number(n.into()), AnyValue::Datetime(dt, tu, tz) => { // 转为RFC3339格式字符串,可根据需求调整输出格式 let naive_dt = dt.to_naive_datetime(tu); if let Some(tz) = tz { let dt_with_tz = naive_dt.and_local_timezone(tz).unwrap(); Value::String(dt_with_tz.to_rfc3339()) } else { Value::String(naive_dt.to_rfc3339()) } } // 按需添加其他数据类型的处理逻辑 _ => Value::String(val.to_string()), // 兜底用字符串表示未处理的类型 } } fn main() -> anyhow::Result<()> { let file = File::open("0.parquet").unwrap(); let mut df = ParquetReader::new(file).finish()?; dbg!(df.schema()); let fields = df.fields(); let columns: Vec<&String> = fields.iter().map(|x| x.name()).collect(); df.as_single_chunk_par(); let mut iters = df.iter().map(|s| s.iter()).collect::<Vec<_>>(); for _ in 0..df.height() { let mut row = HashMap::new(); for (column, iter) in zip(&columns, &mut iters) { let value = iter.next().expect("should have as many iterations as rows"); let json_val = any_value_to_json_value(value); row.insert(column.clone(), json_val); } dbg!(&row); let json = serde_json::to_string(&row).unwrap(); dbg!(json); break; } Ok(()) }
方法3:使用Polars的Row类型
Polars的Row类型已实现serde序列化(需启用serde特性),可以直接序列化:
use polars::prelude::*; use std::fs::File; use serde_json::to_string; fn main() -> anyhow::Result<()> { let file = File::open("0.parquet").unwrap(); let mut df = ParquetReader::new(file).finish()?; df.as_single_chunk_par(); // 获取行迭代器并取第一行 let mut row_iter = df.iter_rows(); if let Some(row) = row_iter.next() { let json = to_string(&row)?; dbg!(json); } Ok(()) }
内容的提问来源于stack exchange,提问作者Al Johri
相关产品推荐
相关产品推荐

