1#![doc(
79 html_logo_url = "https://arrow.apache.org/img/arrow-logo_chevrons_black-txt_white-bg.svg",
80 html_favicon_url = "https://arrow.apache.org/img/arrow-logo_chevrons_black-txt_transparent-bg.svg"
81)]
82#![cfg_attr(docsrs, feature(doc_cfg))]
83#![deny(clippy::allow_attributes)]
84#![deny(rustdoc::broken_intra_doc_links)]
85#![warn(missing_docs)]
86
87pub mod reader;
88pub mod writer;
89
90pub use self::reader::{Reader, ReaderBuilder};
91pub use self::writer::{
92 ArrayWriter, Encoder, EncoderFactory, EncoderOptions, LineDelimitedWriter, Writer,
93 WriterBuilder,
94};
95use half::f16;
96use serde_json::{Number, Value};
97
98#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)]
119pub enum StructMode {
120 #[default]
121 ObjectOnly,
123 ListOnly,
125}
126
127pub trait JsonSerializable: 'static {
129 fn into_json_value(self) -> Option<Value>;
131}
132
133macro_rules! json_serializable {
134 ($t:ty) => {
135 impl JsonSerializable for $t {
136 fn into_json_value(self) -> Option<Value> {
137 Some(self.into())
138 }
139 }
140 };
141}
142
143json_serializable!(bool);
144json_serializable!(u8);
145json_serializable!(u16);
146json_serializable!(u32);
147json_serializable!(u64);
148json_serializable!(i8);
149json_serializable!(i16);
150json_serializable!(i32);
151json_serializable!(i64);
152
153impl JsonSerializable for i128 {
154 fn into_json_value(self) -> Option<Value> {
155 Some(self.to_string().into())
160 }
161}
162
163impl JsonSerializable for f16 {
164 fn into_json_value(self) -> Option<Value> {
165 Number::from_f64(f64::round(f64::from(self) * 1000.0) / 1000.0).map(Value::Number)
166 }
167}
168
169impl JsonSerializable for f32 {
170 fn into_json_value(self) -> Option<Value> {
171 Number::from_f64(f64::round(self as f64 * 1000.0) / 1000.0).map(Value::Number)
172 }
173}
174
175impl JsonSerializable for f64 {
176 fn into_json_value(self) -> Option<Value> {
177 Number::from_f64(self).map(Value::Number)
178 }
179}
180
181#[cfg(test)]
182mod tests {
183 use super::*;
184 use crate::writer::JsonArray;
185 use crate::writer::LineDelimited;
186 use arrow_array::{
187 ArrayRef, GenericBinaryArray, GenericByteViewArray, GenericListViewArray, RecordBatch,
188 RecordBatchWriter, builder::FixedSizeBinaryBuilder, types::BinaryViewType,
189 };
190 use arrow_schema::{DataType, Field, Fields, Schema};
191 use serde_json::Value::{Bool, Number as VNumber, String as VString};
192 use std::io::Cursor;
193 use std::sync::Arc;
194
195 #[test]
196 fn test_arrow_native_type_to_json() {
197 assert_eq!(Some(Bool(true)), true.into_json_value());
198 assert_eq!(Some(VNumber(Number::from(1))), 1i8.into_json_value());
199 assert_eq!(Some(VNumber(Number::from(1))), 1i16.into_json_value());
200 assert_eq!(Some(VNumber(Number::from(1))), 1i32.into_json_value());
201 assert_eq!(Some(VNumber(Number::from(1))), 1i64.into_json_value());
202 assert_eq!(Some(VString("1".to_string())), 1i128.into_json_value());
203 assert_eq!(Some(VNumber(Number::from(1))), 1u8.into_json_value());
204 assert_eq!(Some(VNumber(Number::from(1))), 1u16.into_json_value());
205 assert_eq!(Some(VNumber(Number::from(1))), 1u32.into_json_value());
206 assert_eq!(Some(VNumber(Number::from(1))), 1u64.into_json_value());
207 assert_eq!(
208 Some(VNumber(Number::from_f64(0.01f64).unwrap())),
209 0.01.into_json_value()
210 );
211 assert_eq!(
212 Some(VNumber(Number::from_f64(0.01f64).unwrap())),
213 0.01f64.into_json_value()
214 );
215 assert_eq!(None, f32::NAN.into_json_value());
216 }
217
218 #[test]
219 fn test_json_roundtrip_structs() {
220 let schema = Arc::new(Schema::new(vec![
221 Field::new(
222 "c1",
223 DataType::Struct(Fields::from(vec![
224 Field::new("c11", DataType::Int32, true),
225 Field::new(
226 "c12",
227 DataType::Struct(vec![Field::new("c121", DataType::Utf8, false)].into()),
228 false,
229 ),
230 ])),
231 false,
232 ),
233 Field::new("c2", DataType::Utf8, false),
234 ]));
235
236 {
237 let object_input = r#"{"c1":{"c11":1,"c12":{"c121":"e"}},"c2":"a"}
238{"c1":{"c12":{"c121":"f"}},"c2":"b"}
239{"c1":{"c11":5,"c12":{"c121":"g"}},"c2":"c"}
240"#
241 .as_bytes();
242 let object_reader = ReaderBuilder::new(schema.clone())
243 .with_struct_mode(StructMode::ObjectOnly)
244 .build(object_input)
245 .unwrap();
246
247 let mut object_output: Vec<u8> = Vec::new();
248 let mut object_writer = WriterBuilder::new()
249 .with_struct_mode(StructMode::ObjectOnly)
250 .build::<_, LineDelimited>(&mut object_output);
251 for batch_res in object_reader {
252 object_writer.write(&batch_res.unwrap()).unwrap();
253 }
254 assert_eq!(object_input, &object_output);
255 }
256
257 {
258 let list_input = r#"[[1,["e"]],"a"]
259[[null,["f"]],"b"]
260[[5,["g"]],"c"]
261"#
262 .as_bytes();
263 let list_reader = ReaderBuilder::new(schema.clone())
264 .with_struct_mode(StructMode::ListOnly)
265 .build(list_input)
266 .unwrap();
267
268 let mut list_output: Vec<u8> = Vec::new();
269 let mut list_writer = WriterBuilder::new()
270 .with_struct_mode(StructMode::ListOnly)
271 .build::<_, LineDelimited>(&mut list_output);
272 for batch_res in list_reader {
273 list_writer.write(&batch_res.unwrap()).unwrap();
274 }
275 assert_eq!(list_input, &list_output);
276 }
277 }
278
279 #[test]
280 #[expect(invalid_from_utf8)]
281 fn test_json_roundtrip_binary() {
282 let not_utf8: &[u8] = b"Not UTF8 \xa0\xa1!";
283 assert!(str::from_utf8(not_utf8).is_err());
284
285 let values: &[Option<&[u8]>] = &[
286 Some(b"Ned Flanders" as &[u8]),
287 None,
288 Some(b"Troy McClure" as &[u8]),
289 Some(not_utf8),
290 ];
291 assert_binary_json(Arc::new(GenericBinaryArray::<i32>::from_iter(values)));
293
294 assert_binary_json(Arc::new(GenericBinaryArray::<i64>::from_iter(values)));
296
297 assert_binary_json(build_array_fixed_size_binary(12, values));
299
300 assert_binary_json(Arc::new(GenericByteViewArray::<BinaryViewType>::from_iter(
302 values,
303 )));
304 }
305
306 fn build_array_fixed_size_binary(byte_width: i32, values: &[Option<&[u8]>]) -> ArrayRef {
307 let mut builder = FixedSizeBinaryBuilder::new(byte_width);
308 for value in values {
309 match value {
310 Some(v) => builder.append_value(v).unwrap(),
311 None => builder.append_null(),
312 }
313 }
314 Arc::new(builder.finish())
315 }
316
317 fn assert_binary_json(array: ArrayRef) {
318 assert_binary_json_with_writer(
320 array.clone(),
321 WriterBuilder::new().with_explicit_nulls(true),
322 );
323 assert_binary_json_with_writer(array, WriterBuilder::new().with_explicit_nulls(false));
324 }
325
326 fn assert_binary_json_with_writer(array: ArrayRef, builder: WriterBuilder) {
327 let batch = RecordBatch::try_from_iter([("bytes", array)]).unwrap();
328
329 let mut buf = Vec::new();
330 let json_value: Value = {
331 let mut writer = builder.build::<_, JsonArray>(&mut buf);
332 writer.write(&batch).unwrap();
333 writer.close().unwrap();
334 serde_json::from_slice(&buf).unwrap()
335 };
336
337 let json_array = json_value.as_array().unwrap();
338
339 let decoded = {
340 let mut decoder = ReaderBuilder::new(batch.schema().clone())
341 .build_decoder()
342 .unwrap();
343 decoder.serialize(json_array).unwrap();
344 decoder.flush().unwrap().unwrap()
345 };
346
347 assert_eq!(batch, decoded);
348 }
349
350 fn assert_list_view_roundtrip<O: arrow_array::OffsetSizeTrait>() {
351 let flat_field = Arc::new(Field::new("item", DataType::Int32, true));
352 let flat_dt = GenericListViewArray::<O>::DATA_TYPE_CONSTRUCTOR(flat_field);
353
354 let nested_inner = Arc::new(Field::new("item", DataType::Int32, false));
355 let nested_inner_dt = GenericListViewArray::<O>::DATA_TYPE_CONSTRUCTOR(nested_inner);
356 let nested_outer = Arc::new(Field::new("item", nested_inner_dt, true));
357 let nested_dt = GenericListViewArray::<O>::DATA_TYPE_CONSTRUCTOR(nested_outer);
358
359 let schema = Arc::new(Schema::new(vec![
360 Field::new("flat", flat_dt, true),
361 Field::new("nested", nested_dt, true),
362 ]));
363
364 let input = r#"{"flat":[1,2,3],"nested":[[1,2],[3]]}
365{"flat":[4,null]}
366{}
367{"flat":[6],"nested":[[4,5,6]]}
368{"flat":[]}
369"#
370 .as_bytes();
371
372 let batches: Vec<RecordBatch> = ReaderBuilder::new(schema.clone())
373 .with_batch_size(1024)
374 .build(Cursor::new(input))
375 .unwrap()
376 .collect::<Result<Vec<_>, _>>()
377 .unwrap();
378
379 let mut output = Vec::new();
380 let mut writer = WriterBuilder::new().build::<_, LineDelimited>(&mut output);
381 for batch in &batches {
382 writer.write(batch).unwrap();
383 }
384 writer.finish().unwrap();
385
386 assert_eq!(input, &output);
387 }
388
389 #[test]
390 fn test_json_roundtrip_list_view() {
391 assert_list_view_roundtrip::<i32>();
392 assert_list_view_roundtrip::<i64>();
393 }
394
395 #[test]
396 fn test_json_roundtrip_fixed_size_list() {
397 let inner = Arc::new(Field::new("item", DataType::Int32, true));
398 let schema = Arc::new(Schema::new(vec![
399 Field::new("flat", DataType::FixedSizeList(inner.clone(), 3), true),
400 Field::new(
401 "nested",
402 DataType::FixedSizeList(
403 Arc::new(Field::new("item", DataType::FixedSizeList(inner, 2), true)),
404 2,
405 ),
406 true,
407 ),
408 ]));
409
410 let input = r#"{"flat":[1,2,3],"nested":[[1,2],[3,4]]}
411{"flat":[4,null,5]}
412{"flat":[6,7,8],"nested":[[null,5],[6,null]]}
413"#
414 .as_bytes();
415
416 let batches: Vec<RecordBatch> = ReaderBuilder::new(schema.clone())
417 .with_batch_size(1024)
418 .build(Cursor::new(input))
419 .unwrap()
420 .collect::<Result<Vec<_>, _>>()
421 .unwrap();
422
423 let mut output = Vec::new();
424 let mut writer = WriterBuilder::new().build::<_, LineDelimited>(&mut output);
425 for batch in &batches {
426 writer.write(batch).unwrap();
427 }
428 writer.finish().unwrap();
429
430 assert_eq!(input, &output);
431 }
432}