diff --git a/arrow-cast/Cargo.toml b/arrow-cast/Cargo.toml index 4046f5226094..76eff2f3875c 100644 --- a/arrow-cast/Cargo.toml +++ b/arrow-cast/Cargo.toml @@ -39,6 +39,7 @@ features = ["prettyprint"] [features] prettyprint = ["comfy-table"] force_validate = [] +struct_display_json =[] [dependencies] arrow-array = { workspace = true } @@ -54,6 +55,7 @@ atoi = "2.0.0" comfy-table = { version = "7.0", optional = true, default-features = false } base64 = "0.22" ryu = "1.0.16" +serde_json = "1.0" [dev-dependencies] criterion = { version = "0.5", default-features = false } diff --git a/arrow-cast/src/display.rs b/arrow-cast/src/display.rs index 669b8a664c2b..ad62d7dc7cb8 100644 --- a/arrow-cast/src/display.rs +++ b/arrow-cast/src/display.rs @@ -23,8 +23,10 @@ //! record batch pretty printing. //! //! [`pretty`]: crate::pretty +use std::collections::BTreeMap; use std::fmt::{Display, Formatter, Write}; use std::ops::Range; +use std::str::FromStr; use arrow_array::cast::*; use arrow_array::temporal_conversions::*; @@ -938,7 +940,33 @@ impl<'a> DisplayIndexState<'a> for &'a StructArray { }) .collect() } + #[cfg(feature = "struct_display_json")] + fn write(&self, s: &Self::State, idx: usize, f: &mut dyn Write) -> FormatResult { + let mut iter = s.iter(); + let mut json = BTreeMap::::new(); + if let Some((name, display)) = iter.next() { + let mut display_str = String::new(); + display.write(idx, &mut display_str)?; + json.insert( + name.to_string(), + serde_json::Value::from_str(&display_str) + .unwrap_or(serde_json::Value::String(display_str)), + ); + } + for (name, display) in iter { + let mut display_str = String::new(); + display.write(idx, &mut display_str)?; + json.insert( + name.to_string(), + serde_json::Value::from_str(&display_str) + .unwrap_or(serde_json::Value::String(display_str)), + ); + } + let _ = f.write_str(&serde_json::to_string(&json).unwrap_or_default()); + Ok(()) + } + #[cfg(not(feature = "struct_display_json"))] fn write(&self, s: &Self::State, idx: usize, f: &mut dyn Write) -> FormatResult { let mut iter = s.iter(); f.write_char('{')?; diff --git a/arrow-csv/src/writer.rs b/arrow-csv/src/writer.rs index c5a0a0b76d59..57a48e332e3d 100644 --- a/arrow-csv/src/writer.rs +++ b/arrow-csv/src/writer.rs @@ -93,6 +93,9 @@ pub struct Writer { beginning: bool, /// The value to represent null entries, defaults to [`DEFAULT_NULL_VALUE`] null_value: Option, + + /// Show nested types to csv + show_nested: bool, } impl Writer { @@ -132,7 +135,7 @@ impl Writer { .columns() .iter() .map(|a| { - if a.data_type().is_nested() { + if a.data_type().is_nested() && !self.show_nested { Err(ArrowError::CsvError(format!( "Nested type {} is not supported in CSV", a.data_type() @@ -211,6 +214,8 @@ pub struct WriterBuilder { time_format: Option, /// Optional value to represent null null_value: Option, + /// Show nested types to csv + show_nested: bool, } impl Default for WriterBuilder { @@ -227,6 +232,7 @@ impl Default for WriterBuilder { timestamp_tz_format: None, time_format: None, null_value: None, + show_nested: false, } } } @@ -389,6 +395,12 @@ impl WriterBuilder { self.null_value.as_deref().unwrap_or(DEFAULT_NULL_VALUE) } + /// Set whether to show nested fields + pub fn with_show_nested(mut self, show_nested: bool) -> Self { + self.show_nested = show_nested; + self + } + /// Create a new `Writer` pub fn build(self, writer: W) -> Writer { let mut builder = csv::WriterBuilder::new(); @@ -408,6 +420,7 @@ impl WriterBuilder { timestamp_format: self.timestamp_format, timestamp_tz_format: self.timestamp_tz_format, null_value: self.null_value, + show_nested: self.show_nested, } } } diff --git a/arrow/Cargo.toml b/arrow/Cargo.toml index a1c9c0ab2113..d92cab5bf5ca 100644 --- a/arrow/Cargo.toml +++ b/arrow/Cargo.toml @@ -66,6 +66,8 @@ csv = ["arrow-csv"] ipc = ["arrow-ipc"] json = ["arrow-json"] prettyprint = ["arrow-cast/prettyprint"] +struct_display_json =["arrow-cast/struct_display_json"] + # The test utils feature enables code used in benchmarks and tests but # not the core arrow code itself. Be aware that `rand` must be kept as # an optional dependency for supporting compile to wasm32-unknown-unknown