1use super::ValueIter;
19use arrow_schema::{ArrowError, DataType, Field, Fields, Schema};
20use indexmap::map::IndexMap as HashMap;
21use indexmap::set::IndexSet as HashSet;
22use serde_json::Value;
23use std::borrow::Borrow;
24use std::io::{BufRead, Seek};
25use std::sync::Arc;
26
27#[derive(Debug, Clone)]
28enum InferredType {
29 Scalar(HashSet<DataType>),
30 Array(Box<InferredType>),
31 Object(HashMap<String, InferredType>),
32 Any,
33}
34
35impl InferredType {
36 fn merge(&mut self, other: InferredType) -> Result<(), ArrowError> {
37 match (self, other) {
38 (InferredType::Array(s), InferredType::Array(o)) => {
39 s.merge(*o)?;
40 }
41 (InferredType::Scalar(self_hs), InferredType::Scalar(other_hs)) => {
42 other_hs.into_iter().for_each(|v| {
43 self_hs.insert(v);
44 });
45 }
46 (InferredType::Object(self_map), InferredType::Object(other_map)) => {
47 for (k, v) in other_map {
48 self_map.entry(k).or_insert(InferredType::Any).merge(v)?;
49 }
50 }
51 (s @ InferredType::Any, v) => {
52 *s = v;
53 }
54 (_, InferredType::Any) => {}
55 (InferredType::Array(self_inner_type), other_scalar @ InferredType::Scalar(_)) => {
57 self_inner_type.merge(other_scalar)?;
58 }
59 (s @ InferredType::Scalar(_), InferredType::Array(mut other_inner_type)) => {
60 other_inner_type.merge(s.clone())?;
61 *s = InferredType::Array(other_inner_type);
62 }
63 (s, o) => {
65 return Err(ArrowError::JsonError(format!(
66 "Incompatible type found during schema inference: {s:?} v.s. {o:?}",
67 )));
68 }
69 }
70
71 Ok(())
72 }
73
74 fn is_none_or_any(ty: Option<&Self>) -> bool {
75 matches!(ty, Some(Self::Any) | None)
76 }
77}
78
79fn list_type_of(ty: DataType) -> DataType {
81 DataType::List(Arc::new(Field::new_list_field(ty, true)))
82}
83
84fn coerce_data_type(dt: Vec<&DataType>) -> DataType {
90 let mut dt_iter = dt.into_iter().cloned();
91 let dt_init = dt_iter.next().unwrap_or(DataType::Utf8);
92
93 dt_iter.fold(dt_init, |l, r| match (l, r) {
94 (DataType::Null, o) | (o, DataType::Null) => o,
95 (DataType::Boolean, DataType::Boolean) => DataType::Boolean,
96 (DataType::Int64, DataType::Int64) => DataType::Int64,
97 (DataType::Float64 | DataType::Int64, DataType::Float64)
98 | (DataType::Float64, DataType::Int64) => DataType::Float64,
99 (DataType::List(l), DataType::List(r)) => {
100 list_type_of(coerce_data_type(vec![l.data_type(), r.data_type()]))
101 }
102 (DataType::List(e), not_list) | (not_list, DataType::List(e)) => {
104 list_type_of(coerce_data_type(vec![e.data_type(), ¬_list]))
105 }
106 _ => DataType::Utf8,
107 })
108}
109
110fn generate_datatype(t: &InferredType) -> Result<DataType, ArrowError> {
111 Ok(match t {
112 InferredType::Scalar(hs) => coerce_data_type(hs.iter().collect()),
113 InferredType::Object(spec) => DataType::Struct(generate_fields(spec)?),
114 InferredType::Array(ele_type) => list_type_of(generate_datatype(ele_type)?),
115 InferredType::Any => DataType::Null,
116 })
117}
118
119fn generate_fields(spec: &HashMap<String, InferredType>) -> Result<Fields, ArrowError> {
120 spec.iter()
121 .map(|(k, types)| Ok(Field::new(k, generate_datatype(types)?, true)))
122 .collect()
123}
124
125fn generate_schema(spec: HashMap<String, InferredType>) -> Result<Schema, ArrowError> {
127 Ok(Schema::new(generate_fields(&spec)?))
128}
129
130pub fn infer_json_schema_from_seekable<R: BufRead + Seek>(
155 mut reader: R,
156 max_read_records: Option<usize>,
157) -> Result<(Schema, usize), ArrowError> {
158 let schema = infer_json_schema(&mut reader, max_read_records);
159 reader.rewind()?;
161
162 schema
163}
164
165pub fn infer_json_schema<R: BufRead>(
203 reader: R,
204 max_read_records: Option<usize>,
205) -> Result<(Schema, usize), ArrowError> {
206 let mut values = ValueIter::new(reader, max_read_records);
207 let schema = infer_json_schema_from_iterator(&mut values)?;
208 Ok((schema, values.record_count()))
209}
210
211fn set_object_scalar_field_type(
212 field_types: &mut HashMap<String, InferredType>,
213 key: &str,
214 ftype: DataType,
215) -> Result<(), ArrowError> {
216 if InferredType::is_none_or_any(field_types.get(key)) {
217 field_types.insert(key.to_string(), InferredType::Scalar(HashSet::new()));
218 }
219
220 match field_types.get_mut(key).unwrap() {
221 InferredType::Scalar(hs) => {
222 hs.insert(ftype);
223 Ok(())
224 }
225 scalar_array @ InferredType::Array(_) => {
228 let mut hs = HashSet::new();
229 hs.insert(ftype);
230 scalar_array.merge(InferredType::Scalar(hs))?;
231 Ok(())
232 }
233 t => Err(ArrowError::JsonError(format!(
234 "Expected scalar or scalar array JSON type, found: {t:?}",
235 ))),
236 }
237}
238
239fn infer_scalar_array_type(array: &[Value]) -> Result<InferredType, ArrowError> {
240 let mut hs = HashSet::new();
241
242 for v in array {
243 match v {
244 Value::Null => {}
245 Value::Number(n) => {
246 if n.is_i64() {
247 hs.insert(DataType::Int64);
248 } else {
249 hs.insert(DataType::Float64);
250 }
251 }
252 Value::Bool(_) => {
253 hs.insert(DataType::Boolean);
254 }
255 Value::String(_) => {
256 hs.insert(DataType::Utf8);
257 }
258 Value::Array(_) | Value::Object(_) => {
259 return Err(ArrowError::JsonError(format!(
260 "Expected scalar value for scalar array, got: {v:?}"
261 )));
262 }
263 }
264 }
265
266 Ok(InferredType::Scalar(hs))
267}
268
269fn infer_nested_array_type(array: &[Value]) -> Result<InferredType, ArrowError> {
270 let mut inner_ele_type = InferredType::Any;
271
272 for v in array {
273 match v {
274 Value::Array(inner_array) => {
275 inner_ele_type.merge(infer_array_element_type(inner_array)?)?;
276 }
277 x => {
278 return Err(ArrowError::JsonError(format!(
279 "Got non array element in nested array: {x:?}"
280 )));
281 }
282 }
283 }
284
285 Ok(InferredType::Array(Box::new(inner_ele_type)))
286}
287
288fn infer_struct_array_type(array: &[Value]) -> Result<InferredType, ArrowError> {
289 let mut field_types = HashMap::new();
290
291 for v in array {
292 match v {
293 Value::Object(map) => {
294 collect_field_types_from_object(&mut field_types, map)?;
295 }
296 _ => {
297 return Err(ArrowError::JsonError(format!(
298 "Expected struct value for struct array, got: {v:?}"
299 )));
300 }
301 }
302 }
303
304 Ok(InferredType::Object(field_types))
305}
306
307fn infer_array_element_type(array: &[Value]) -> Result<InferredType, ArrowError> {
308 match array.iter().take(1).next() {
309 None => Ok(InferredType::Any), Some(a) => match a {
311 Value::Array(_) => infer_nested_array_type(array),
312 Value::Object(_) => infer_struct_array_type(array),
313 _ => infer_scalar_array_type(array),
314 },
315 }
316}
317
318fn collect_field_types_from_object(
319 field_types: &mut HashMap<String, InferredType>,
320 map: &serde_json::map::Map<String, Value>,
321) -> Result<(), ArrowError> {
322 for (k, v) in map {
323 match v {
324 Value::Array(array) => {
325 let ele_type = infer_array_element_type(array)?;
326
327 if InferredType::is_none_or_any(field_types.get(k)) {
328 match ele_type {
329 InferredType::Scalar(_) => {
330 field_types.insert(
331 k.clone(),
332 InferredType::Array(Box::new(InferredType::Scalar(HashSet::new()))),
333 );
334 }
335 InferredType::Object(_) => {
336 field_types.insert(
337 k.clone(),
338 InferredType::Array(Box::new(InferredType::Object(HashMap::new()))),
339 );
340 }
341 InferredType::Any | InferredType::Array(_) => {
342 field_types.insert(
345 k.clone(),
346 InferredType::Array(Box::new(InferredType::Any)),
347 );
348 }
349 }
350 }
351
352 match field_types.get_mut(k).unwrap() {
353 InferredType::Array(inner_type) => {
354 inner_type.merge(ele_type)?;
355 }
356 field_type @ InferredType::Scalar(_) => {
359 field_type.merge(ele_type)?;
360 *field_type = InferredType::Array(Box::new(field_type.clone()));
361 }
362 t => {
363 return Err(ArrowError::JsonError(format!(
364 "Expected array json type, found: {t:?}",
365 )));
366 }
367 }
368 }
369 Value::Bool(_) => {
370 set_object_scalar_field_type(field_types, k, DataType::Boolean)?;
371 }
372 Value::Null => {
373 if !field_types.contains_key(k) {
376 field_types.insert(k.clone(), InferredType::Any);
377 }
378 }
379 Value::Number(n) => {
380 if n.is_i64() {
381 set_object_scalar_field_type(field_types, k, DataType::Int64)?;
382 } else {
383 set_object_scalar_field_type(field_types, k, DataType::Float64)?;
384 }
385 }
386 Value::String(_) => {
387 set_object_scalar_field_type(field_types, k, DataType::Utf8)?;
388 }
389 Value::Object(inner_map) => {
390 if matches!(
391 field_types.get(k).unwrap_or(&InferredType::Any),
392 InferredType::Any
393 ) {
394 field_types.insert(k.clone(), InferredType::Object(HashMap::new()));
395 }
396 match field_types.get_mut(k).unwrap() {
397 InferredType::Object(inner_field_types) => {
398 collect_field_types_from_object(inner_field_types, inner_map)?;
399 }
400 t => {
401 return Err(ArrowError::JsonError(format!(
402 "Expected object json type, found: {t:?}",
403 )));
404 }
405 }
406 }
407 }
408 }
409
410 Ok(())
411}
412
413pub fn infer_json_schema_from_iterator<I, V>(value_iter: I) -> Result<Schema, ArrowError>
427where
428 I: Iterator<Item = Result<V, ArrowError>>,
429 V: Borrow<Value>,
430{
431 let mut field_types: HashMap<String, InferredType> = HashMap::new();
432
433 for record in value_iter {
434 match record?.borrow() {
435 Value::Object(map) => {
436 collect_field_types_from_object(&mut field_types, map)?;
437 }
438 value => {
439 return Err(ArrowError::JsonError(format!(
440 "Expected JSON record to be an object, found {value:?}"
441 )));
442 }
443 }
444 }
445
446 generate_schema(field_types)
447}
448
449#[cfg(test)]
450mod tests {
451 use super::*;
452 use flate2::read::GzDecoder;
453 use std::fs::File;
454 use std::io::{BufReader, Cursor};
455
456 #[test]
457 fn test_json_infer_schema() {
458 let schema = Schema::new(vec![
459 Field::new("a", DataType::Int64, true),
460 Field::new("b", list_type_of(DataType::Float64), true),
461 Field::new("c", list_type_of(DataType::Boolean), true),
462 Field::new("d", list_type_of(DataType::Utf8), true),
463 ]);
464
465 let mut reader = BufReader::new(File::open("test/data/mixed_arrays.json").unwrap());
466 let (inferred_schema, n_rows) = infer_json_schema_from_seekable(&mut reader, None).unwrap();
467
468 assert_eq!(inferred_schema, schema);
469 assert_eq!(n_rows, 4);
470
471 let file = File::open("test/data/mixed_arrays.json.gz").unwrap();
472 let mut reader = BufReader::new(GzDecoder::new(&file));
473 let (inferred_schema, n_rows) = infer_json_schema(&mut reader, None).unwrap();
474
475 assert_eq!(inferred_schema, schema);
476 assert_eq!(n_rows, 4);
477 }
478
479 #[test]
480 fn test_row_limit() {
481 let mut reader = BufReader::new(File::open("test/data/basic.json").unwrap());
482
483 let (_, n_rows) = infer_json_schema_from_seekable(&mut reader, None).unwrap();
484 assert_eq!(n_rows, 12);
485
486 let (_, n_rows) = infer_json_schema_from_seekable(&mut reader, Some(5)).unwrap();
487 assert_eq!(n_rows, 5);
488 }
489
490 #[test]
491 fn test_json_infer_schema_nested_structs() {
492 let schema = Schema::new(vec![
493 Field::new(
494 "c1",
495 DataType::Struct(Fields::from(vec![
496 Field::new("a", DataType::Boolean, true),
497 Field::new(
498 "b",
499 DataType::Struct(vec![Field::new("c", DataType::Utf8, true)].into()),
500 true,
501 ),
502 ])),
503 true,
504 ),
505 Field::new("c2", DataType::Int64, true),
506 Field::new("c3", DataType::Utf8, true),
507 ]);
508
509 let inferred_schema = infer_json_schema_from_iterator(
510 vec![
511 Ok(serde_json::json!({"c1": {"a": true, "b": {"c": "text"}}, "c2": 1})),
512 Ok(serde_json::json!({"c1": {"a": false, "b": null}, "c2": 0})),
513 Ok(serde_json::json!({"c1": {"a": true, "b": {"c": "text"}}, "c3": "ok"})),
514 ]
515 .into_iter(),
516 )
517 .unwrap();
518
519 assert_eq!(inferred_schema, schema);
520 }
521
522 #[test]
523 fn test_json_infer_schema_struct_in_list() {
524 let schema = Schema::new(vec![
525 Field::new(
526 "c1",
527 list_type_of(DataType::Struct(Fields::from(vec![
528 Field::new("a", DataType::Utf8, true),
529 Field::new("b", DataType::Int64, true),
530 Field::new("c", DataType::Boolean, true),
531 ]))),
532 true,
533 ),
534 Field::new("c2", DataType::Float64, true),
535 Field::new(
536 "c3",
537 list_type_of(DataType::Null),
539 true,
540 ),
541 ]);
542
543 let inferred_schema = infer_json_schema_from_iterator(
544 vec![
545 Ok(serde_json::json!({
546 "c1": [{"a": "foo", "b": 100}], "c2": 1, "c3": [],
547 })),
548 Ok(serde_json::json!({
549 "c1": [{"a": "bar", "b": 2}, {"a": "foo", "c": true}], "c2": 0, "c3": [],
550 })),
551 Ok(serde_json::json!({"c1": [], "c2": 0.5, "c3": []})),
552 ]
553 .into_iter(),
554 )
555 .unwrap();
556
557 assert_eq!(inferred_schema, schema);
558 }
559
560 #[test]
561 fn test_json_infer_schema_nested_list() {
562 let schema = Schema::new(vec![
563 Field::new("c1", list_type_of(list_type_of(DataType::Utf8)), true),
564 Field::new("c2", DataType::Float64, true),
565 ]);
566
567 let inferred_schema = infer_json_schema_from_iterator(
568 vec![
569 Ok(serde_json::json!({
570 "c1": [],
571 "c2": 12,
572 })),
573 Ok(serde_json::json!({
574 "c1": [["a", "b"], ["c"]],
575 })),
576 Ok(serde_json::json!({
577 "c1": [["foo"]],
578 "c2": 0.11,
579 })),
580 ]
581 .into_iter(),
582 )
583 .unwrap();
584
585 assert_eq!(inferred_schema, schema);
586 }
587
588 #[test]
589 fn test_infer_json_schema_bigger_than_i64_max() {
590 let bigger_than_i64_max = (i64::MAX as i128) + 1;
591 let smaller_than_i64_min = (i64::MIN as i128) - 1;
592 let json = format!(
593 "{{ \"bigger_than_i64_max\": {bigger_than_i64_max}, \"smaller_than_i64_min\": {smaller_than_i64_min} }}",
594 );
595 let mut buf_reader = BufReader::new(json.as_bytes());
596 let (inferred_schema, _) = infer_json_schema(&mut buf_reader, Some(1)).unwrap();
597 let fields = inferred_schema.fields();
598
599 let (_, big_field) = fields.find("bigger_than_i64_max").unwrap();
600 assert_eq!(big_field.data_type(), &DataType::Float64);
601 let (_, small_field) = fields.find("smaller_than_i64_min").unwrap();
602 assert_eq!(small_field.data_type(), &DataType::Float64);
603 }
604
605 #[test]
606 fn test_coercion_scalar_and_list() {
607 assert_eq!(
608 list_type_of(DataType::Float64),
609 coerce_data_type(vec![&DataType::Float64, &list_type_of(DataType::Float64)])
610 );
611 assert_eq!(
612 list_type_of(DataType::Float64),
613 coerce_data_type(vec![&DataType::Float64, &list_type_of(DataType::Int64)])
614 );
615 assert_eq!(
616 list_type_of(DataType::Int64),
617 coerce_data_type(vec![&DataType::Int64, &list_type_of(DataType::Int64)])
618 );
619 assert_eq!(
621 list_type_of(DataType::Utf8),
622 coerce_data_type(vec![&DataType::Boolean, &list_type_of(DataType::Float64)])
623 );
624 }
625
626 #[test]
627 fn test_invalid_json_infer_schema() {
628 let re = infer_json_schema_from_seekable(Cursor::new(b"}"), None);
629 assert_eq!(
630 re.err().unwrap().to_string(),
631 "Json error: Not valid JSON: expected value at line 1 column 1",
632 );
633 }
634
635 #[test]
636 fn test_null_field_inferred_as_null() {
637 let data = r#"
638 {"in":1, "ni":null, "ns":null, "sn":"4", "n":null, "an":[], "na": null, "nas":null}
639 {"in":null, "ni":2, "ns":"3", "sn":null, "n":null, "an":null, "na": [], "nas":["8"]}
640 {"in":1, "ni":null, "ns":null, "sn":"4", "n":null, "an":[], "na": null, "nas":[]}
641 "#;
642 let (inferred_schema, _) =
643 infer_json_schema_from_seekable(Cursor::new(data), None).expect("infer");
644 let schema = Schema::new(vec![
645 Field::new("an", list_type_of(DataType::Null), true),
646 Field::new("in", DataType::Int64, true),
647 Field::new("n", DataType::Null, true),
648 Field::new("na", list_type_of(DataType::Null), true),
649 Field::new("nas", list_type_of(DataType::Utf8), true),
650 Field::new("ni", DataType::Int64, true),
651 Field::new("ns", DataType::Utf8, true),
652 Field::new("sn", DataType::Utf8, true),
653 ]);
654 assert_eq!(inferred_schema, schema);
655 }
656
657 #[test]
658 fn test_infer_from_null_then_object() {
659 let data = r#"
660 {"obj":null}
661 {"obj":{"foo":1}}
662 "#;
663 let (inferred_schema, _) =
664 infer_json_schema_from_seekable(Cursor::new(data), None).expect("infer");
665 let schema = Schema::new(vec![Field::new(
666 "obj",
667 DataType::Struct(std::iter::once(Field::new("foo", DataType::Int64, true)).collect()),
668 true,
669 )]);
670 assert_eq!(inferred_schema, schema);
671 }
672}