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