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