1pub mod value;
23
24use std::collections::BTreeMap;
25use std::collections::btree_map::Entry;
26use std::sync::Arc;
27
28use serde::{Deserialize, Serialize};
29use serde_json::{Map, Value as Json};
30use snafu::ResultExt;
31
32use crate::data_type::ConcreteDataType;
33use crate::error::{self, InvalidJson2SettingsSnafu, Result, UnsupportedJsonTypeSnafu};
34use crate::json::value::{JsonValue, JsonVariant, encode_serde_json_as_jsonb};
35use crate::prelude::DataType as _;
36use crate::types::json_type::{JsonNativeType, JsonObjectType};
37use crate::value::{ListValue, StructValue, Value};
38
39pub const JSON2_MAX_STRUCTURED_DEPTH: usize = 50;
41pub const JSON2_REMAINDER_FIELD_NAME: &str = "!__remainder__!";
43pub const JSON2_DEFAULT_MAX_AUTO_EXPANDED_PATHS: u32 = 100;
45
46#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize)]
49pub struct JsonSettings {
50 #[serde(default)]
51 type_hints: Vec<JsonTypeHint>,
52 #[serde(default, skip_serializing_if = "Option::is_none")]
56 max_auto_expanded_paths: Option<u32>,
57}
58
59#[derive(Deserialize)]
60struct JsonSettingsSerde {
61 #[serde(default)]
62 type_hints: Vec<JsonTypeHint>,
63 #[serde(default)]
64 max_auto_expanded_paths: Option<u32>,
65}
66
67impl<'de> Deserialize<'de> for JsonSettings {
68 fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
69 where
70 D: serde::Deserializer<'de>,
71 {
72 let settings = JsonSettingsSerde::deserialize(deserializer)?;
73 Self::try_new(settings.type_hints, settings.max_auto_expanded_paths)
74 .map_err(serde::de::Error::custom)
75 }
76}
77
78#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
84pub struct JsonTypeHint {
85 pub path: Vec<String>,
93 #[serde(rename = "type")]
94 pub data_type: ConcreteDataType,
95 pub inverted_index: bool,
96 #[serde(default, skip_serializing_if = "Option::is_none")]
98 pub skipping_index: Option<crate::schema::SkippingIndexOptions>,
99}
100
101#[derive(Debug, Clone, Copy, PartialEq, Eq)]
103pub enum TypeHintMismatchPolicy {
104 Reject,
106 CoerceOrNull,
108}
109
110#[derive(Clone, Debug)]
112pub struct JsonContext<'a> {
113 pub path: Vec<String>,
115 pub settings: &'a JsonSettings,
117}
118
119impl JsonSettings {
120 pub fn new_v2() -> Self {
122 Self {
123 type_hints: vec![],
124 max_auto_expanded_paths: Some(JSON2_DEFAULT_MAX_AUTO_EXPANDED_PATHS),
125 }
126 }
127
128 pub fn try_new(
130 type_hints: Vec<JsonTypeHint>,
131 max_auto_expanded_paths: Option<u32>,
132 ) -> Result<Self> {
133 validate_type_hints(&type_hints)?;
134 Ok(Self {
135 type_hints,
136 max_auto_expanded_paths,
137 })
138 }
139
140 pub fn type_hints(&self) -> &[JsonTypeHint] {
141 &self.type_hints
142 }
143
144 pub fn max_auto_expanded_paths(&self) -> Option<u32> {
145 self.max_auto_expanded_paths
146 }
147
148 pub fn into_parts(self) -> (Vec<JsonTypeHint>, Option<u32>) {
149 (self.type_hints, self.max_auto_expanded_paths)
150 }
151
152 pub fn equivalent(&self, other: &Self) -> bool {
154 let Self {
155 type_hints,
156 max_auto_expanded_paths,
157 } = self;
158 let Self {
159 type_hints: other_hints,
160 max_auto_expanded_paths: other_max_auto_expanded_paths,
161 } = other;
162
163 if max_auto_expanded_paths != other_max_auto_expanded_paths
164 || type_hints.len() != other_hints.len()
165 {
166 return false;
167 }
168
169 let mut hints = type_hints.iter().collect::<Vec<_>>();
170 let mut other_hints = other_hints.iter().collect::<Vec<_>>();
171 hints.sort_unstable_by(|a, b| a.path.cmp(&b.path));
172 other_hints.sort_unstable_by(|a, b| a.path.cmp(&b.path));
173 hints == other_hints
174 }
175
176 pub fn decode(&self, value: Value) -> Result<Json> {
178 let mut context = JsonContext {
179 path: Vec::new(),
180 settings: self,
181 };
182 decode_value_with_context(value, &mut context)
183 }
184
185 pub fn encode(&self, json: Json) -> Result<Value> {
187 self.encode_with_type_hint_mismatch_policy(json, TypeHintMismatchPolicy::Reject)
188 }
189
190 pub fn encode_with_type_hint_mismatch_policy(
192 &self,
193 json: Json,
194 policy: TypeHintMismatchPolicy,
195 ) -> Result<Value> {
196 if let Json::Object(object) = &json
197 && object.contains_key(JSON2_REMAINDER_FIELD_NAME)
198 {
199 return error::InvalidJsonSnafu {
200 value: format!(
201 "root object cannot contain reserved field '{JSON2_REMAINDER_FIELD_NAME}'"
202 ),
203 }
204 .fail();
205 }
206 let mut context = JsonContext {
207 path: Vec::new(),
208 settings: self,
209 };
210 encode_json_with_context(json, &mut context, policy).map(|v| Value::Json(Box::new(v)))
211 }
212}
213
214fn validate_type_hints(type_hints: &[JsonTypeHint]) -> Result<()> {
215 if type_hints.is_empty() {
216 return Ok(());
217 }
218
219 let mut object = JsonObjectType::new();
220 for hint in type_hints {
221 if let Some(options) = &hint.skipping_index {
222 crate::schema::SkippingIndexOptions::new(
223 options.granularity,
224 options.false_positive_rate(),
225 options.index_type.clone(),
226 )?;
227 }
228 if hint.path.len() > JSON2_MAX_STRUCTURED_DEPTH {
229 return InvalidJson2SettingsSnafu {
230 reason: format!(
231 "JSON2 type hint path cannot exceed {JSON2_MAX_STRUCTURED_DEPTH} segments"
232 ),
233 }
234 .fail();
235 }
236 for part in &hint.path {
237 if part.is_empty() {
238 return InvalidJson2SettingsSnafu {
239 reason: "JSON2 type hint path segments must not be empty",
240 }
241 .fail();
242 }
243 if part == JSON2_REMAINDER_FIELD_NAME {
244 return InvalidJson2SettingsSnafu {
245 reason: format!(
246 "JSON2 type hint path cannot contain reserved field '{JSON2_REMAINDER_FIELD_NAME}'"
247 ),
248 }
249 .fail();
250 }
251 }
252 let data_type = match &hint.data_type {
253 ConcreteDataType::Boolean(_)
254 | ConcreteDataType::UInt64(_)
255 | ConcreteDataType::Int64(_)
256 | ConcreteDataType::Float64(_)
257 | ConcreteDataType::String(_) => (&hint.data_type).into(),
258 data_type => {
259 return InvalidJson2SettingsSnafu {
260 reason: format!("unsupported JSON2 type hint data type: {data_type}"),
261 }
262 .fail();
263 }
264 };
265 validate_type_hint(&mut object, &hint.path, data_type)?;
266 }
267 Ok(())
268}
269
270fn validate_type_hint(
271 object: &mut JsonObjectType,
272 path: &[String],
273 data_type: JsonNativeType,
274) -> Result<()> {
275 let Some((name, path)) = path.split_first() else {
276 return InvalidJson2SettingsSnafu {
277 reason: "JSON2 type hint path must not be empty".to_string(),
278 }
279 .fail();
280 };
281 if path.is_empty() {
282 if object.insert(name.clone(), data_type).is_some() {
283 return InvalidJson2SettingsSnafu {
284 reason: format!("duplicate JSON2 type hint path '{name}'"),
285 }
286 .fail();
287 }
288 return Ok(());
289 }
290
291 let child = object
292 .entry(name.clone())
293 .or_insert_with(|| JsonNativeType::Object(JsonObjectType::new()));
294 let JsonNativeType::Object(child) = child else {
295 return InvalidJson2SettingsSnafu {
296 reason: format!("conflicting JSON2 type hint path at '{name}'"),
297 }
298 .fail();
299 };
300 validate_type_hint(child, path, data_type)
301}
302
303impl<'a> JsonContext<'a> {
304 fn type_hint(&self) -> Option<&'a JsonTypeHint> {
305 self.settings
306 .type_hints
307 .iter()
308 .find(|hint| hint.path == self.path)
309 }
310}
311
312fn with_key_context<T>(
313 context: &mut JsonContext,
314 key: &str,
315 f: impl FnOnce(&mut JsonContext) -> Result<T>,
316) -> Result<T> {
317 context.path.push(key.to_string());
318 let result = f(context);
319 context.path.pop();
320 result
321}
322
323fn encode_json_with_context(
325 json: Json,
326 context: &mut JsonContext,
327 policy: TypeHintMismatchPolicy,
328) -> Result<JsonValue> {
329 if context.path.is_empty() && !matches!(json, Json::Object(_)) {
330 return UnsupportedJsonTypeSnafu.fail();
331 }
332
333 match json {
334 Json::Object(json_object) => encode_json_object_with_context(json_object, context, policy),
335 Json::Array(json_array) => encode_json_array_with_context(json_array, context, policy),
336 _ => encode_json_value_with_context(json, context, policy),
337 }
338}
339
340fn encode_json_object_with_context<'a>(
341 json_object: Map<String, Json>,
342 context: &mut JsonContext<'a>,
343 policy: TypeHintMismatchPolicy,
344) -> Result<JsonValue> {
345 let mut object = BTreeMap::new();
346 for (key, value) in json_object {
347 let value = with_key_context(context, &key, |context| {
348 if let Some(hint) = context.type_hint() {
349 encode_json_value_with_hint(value, hint, context, policy)
350 } else {
351 encode_json_value_with_context(value, context, policy)
352 }
353 })?;
354
355 object.insert(key, value.into_variant());
356 }
357
358 fill_missing_type_hints(&mut object, context, policy)?;
359
360 Ok(JsonValue::new(JsonVariant::Object(object)))
361}
362
363fn fill_missing_type_hints(
364 object: &mut BTreeMap<String, JsonVariant>,
365 context: &mut JsonContext,
366 policy: TypeHintMismatchPolicy,
367) -> Result<()> {
368 for hint in &context.settings.type_hints {
369 if hint.path.len() > context.path.len() && hint.path.starts_with(&context.path) {
370 let depth = context.path.len();
371 let key = &hint.path[depth];
372 with_key_context(context, key, |context| {
373 insert_missing_type_hint(object, context, hint, depth, policy)
374 })?;
375 }
376 }
377 Ok(())
378}
379
380fn insert_missing_type_hint(
381 object: &mut BTreeMap<String, JsonVariant>,
382 field_context: &mut JsonContext,
383 hint: &JsonTypeHint,
384 depth: usize,
385 policy: TypeHintMismatchPolicy,
386) -> Result<()> {
387 let key = &hint.path[depth];
388 let is_leaf = depth + 1 == hint.path.len();
389
390 if is_leaf {
391 if !object.contains_key(key) {
392 object.insert(key.clone(), JsonValue::null().into_variant());
393 }
394 return Ok(());
395 }
396
397 match object.entry(key.clone()) {
398 Entry::Occupied(mut entry) => match entry.get_mut() {
399 JsonVariant::Object(child) => {
400 insert_missing_type_hint(child, field_context, hint, depth + 1, policy)
401 }
402 _ if policy == TypeHintMismatchPolicy::CoerceOrNull => {
403 entry.insert(JsonValue::null().into_variant());
404 Ok(())
405 }
406 _ => error::InvalidJsonSnafu {
407 value: format!(
408 "JSON2 type hint path {} expects object at {}",
409 hint.path.join("."),
410 field_context.path.join(".")
411 ),
412 }
413 .fail(),
414 },
415 Entry::Vacant(entry) => {
416 let mut child = BTreeMap::new();
417 insert_missing_type_hint(&mut child, field_context, hint, depth + 1, policy)?;
418 entry.insert(JsonVariant::Object(child));
419 Ok(())
420 }
421 }
422}
423
424fn encode_json_value_with_hint(
425 json: Json,
426 hint: &JsonTypeHint,
427 context: &mut JsonContext,
428 policy: TypeHintMismatchPolicy,
429) -> Result<JsonValue> {
430 if json.is_null() {
431 return Ok(JsonValue::null());
432 }
433
434 let json = match (&hint.data_type, json) {
435 (ConcreteDataType::String(_), Json::String(value)) => {
436 return Ok(value.into());
437 }
438 (
439 ConcreteDataType::Int8(_)
440 | ConcreteDataType::Int16(_)
441 | ConcreteDataType::Int32(_)
442 | ConcreteDataType::Int64(_),
443 Json::Number(value),
444 ) => {
445 if let Some(value) = value.as_i64() {
446 return Ok(value.into());
447 }
448 Json::Number(value)
449 }
450 (
451 ConcreteDataType::UInt8(_)
452 | ConcreteDataType::UInt16(_)
453 | ConcreteDataType::UInt32(_)
454 | ConcreteDataType::UInt64(_),
455 Json::Number(value),
456 ) => {
457 if let Some(value) = value.as_u64() {
458 return Ok(value.into());
459 }
460 Json::Number(value)
461 }
462 (ConcreteDataType::Float32(_) | ConcreteDataType::Float64(_), Json::Number(value)) => {
463 if let Some(value) = value.as_f64() {
464 return Ok(value.into());
465 }
466 Json::Number(value)
467 }
468 (ConcreteDataType::Boolean(_), Json::Bool(value)) => {
469 return Ok(value.into());
470 }
471 (_, json) => json,
472 };
473
474 match policy {
475 TypeHintMismatchPolicy::Reject => error::InvalidJsonSnafu {
476 value: format!(
477 "JSON value at {} does not match JSON2 type hint {}",
478 context.path.join("."),
479 hint.data_type
480 ),
481 }
482 .fail(),
483 TypeHintMismatchPolicy::CoerceOrNull => {
484 let value = coerce_json_value_to_type(json, &hint.data_type);
485 let value = Json::try_from(value).map_err(|error| {
486 error::InvalidJsonSnafu {
487 value: error.to_string(),
488 }
489 .build()
490 })?;
491 Ok(JsonValue::new(value.into()))
492 }
493 }
494}
495
496pub(crate) fn coerce_json_value_to_type(value: Json, to_type: &ConcreteDataType) -> Value {
499 if value.is_null() {
500 return Value::Null;
501 }
502
503 if to_type.is_string() {
504 let value = match value {
505 Json::String(value) => value,
506 value => value.to_string(),
507 };
508 return Value::String(value.into());
509 }
510
511 if matches!(to_type, ConcreteDataType::Binary(_)) {
512 return Value::Binary(encode_serde_json_as_jsonb(value).into());
513 }
514
515 if let Some(struct_type) = to_type.as_struct() {
516 let Json::Object(mut object) = value else {
517 return Value::Null;
518 };
519 let values = struct_type
520 .fields()
521 .iter()
522 .map(|field| {
523 object
524 .remove(field.name())
525 .map(|value| coerce_json_value_to_type(value, field.data_type()))
526 .unwrap_or(Value::Null)
527 })
528 .collect::<Vec<_>>();
529 return Value::Struct(StructValue::new(values, struct_type.clone()));
530 }
531
532 if let Some(list_type) = to_type.as_list() {
533 let Json::Array(values) = value else {
534 return Value::Null;
535 };
536 let item_type = list_type.item_type().clone();
537 let values = values
538 .into_iter()
539 .map(|value| coerce_json_value_to_type(value, &item_type))
540 .collect::<Vec<_>>();
541 return Value::List(ListValue::new(values, Arc::new(item_type)));
542 }
543
544 let value = match value {
545 Json::Bool(value) => Value::Boolean(value),
546 Json::Number(value) => {
547 if let Some(value) = value.as_i64() {
548 Value::Int64(value)
549 } else if let Some(value) = value.as_u64() {
550 Value::UInt64(value)
551 } else if let Some(value) = value.as_f64() {
552 Value::Float64(value.into())
553 } else {
554 Value::Null
555 }
556 }
557 Json::String(value) => Value::String(value.into()),
558 Json::Array(_) | Json::Object(_) | Json::Null => Value::Null,
559 };
560 to_type.try_cast(value).unwrap_or(Value::Null)
561}
562
563fn encode_json_array_with_context<'a>(
564 json_array: Vec<Json>,
565 context: &mut JsonContext<'a>,
566 policy: TypeHintMismatchPolicy,
567) -> Result<JsonValue> {
568 let json_array_len = json_array.len();
569 let mut items = Vec::with_capacity(json_array_len);
570
571 for (index, value) in json_array.into_iter().enumerate() {
572 let item_value = with_key_context(context, &index.to_string(), |context| {
573 encode_json_value_with_context(value, context, policy)
574 })?;
575 items.push(item_value);
576 }
577
578 let merged_item_type = if let Some((first, rests)) = items.split_first() {
584 let mut merged = first.json_type().clone();
585 for rest in rests.iter().map(|x| x.json_type()) {
586 if matches!(merged, JsonNativeType::Variant) {
587 break;
588 }
589 merged.merge(rest);
590 }
591 Some(merged)
592 } else {
593 None
594 };
595 if let Some(unified_item_type) = merged_item_type {
596 for item in &mut items {
597 item.try_align(&unified_item_type)?;
598 }
599 }
600 let items = items
601 .into_iter()
602 .map(|x| x.into_variant())
603 .collect::<Vec<_>>();
604 Ok(JsonValue::new(JsonVariant::Array(items)))
605}
606
607fn encode_json_value_with_context(
609 json: Json,
610 context: &mut JsonContext,
611 policy: TypeHintMismatchPolicy,
612) -> Result<JsonValue> {
613 if context.path.len() >= JSON2_MAX_STRUCTURED_DEPTH
614 && matches!(&json, Json::Object(_) | Json::Array(_))
615 {
616 return Ok(JsonValue::new(JsonVariant::Variant(
617 encode_serde_json_as_jsonb(json),
618 )));
619 }
620
621 match json {
622 Json::Null => Ok(JsonValue::null()),
623 Json::Bool(b) => Ok(b.into()),
624 Json::Number(n) => {
625 if let Some(i) = n.as_i64() {
626 Ok(i.into())
627 } else if let Some(u) = n.as_u64() {
628 if u <= i64::MAX as u64 {
629 Ok((u as i64).into())
630 } else {
631 Ok(u.into())
632 }
633 } else if let Some(f) = n.as_f64() {
634 Ok(f.into())
635 } else {
636 Ok(n.to_string().into())
638 }
639 }
640 Json::String(s) => Ok(s.into()),
641 Json::Array(arr) => encode_json_array_with_context(arr, context, policy),
642 Json::Object(obj) => encode_json_object_with_context(obj, context, policy),
643 }
644}
645
646fn decode_value_with_context(value: Value, context: &mut JsonContext) -> Result<Json> {
648 match value {
649 Value::Struct(struct_value) => decode_struct_with_context(struct_value, context),
650 Value::List(list_value) => decode_list_with_context(list_value, context),
651 _ => decode_primitive_value(value),
652 }
653}
654
655fn decode_struct_with_context<'a>(
657 struct_value: StructValue,
658 context: &mut JsonContext<'a>,
659) -> Result<Json> {
660 let mut json_object = Map::with_capacity(struct_value.len());
661
662 let (items, fields) = struct_value.into_parts();
663
664 for (field, field_value) in fields.fields().iter().zip(items) {
665 let json_value = with_key_context(context, field.name(), |context| {
666 decode_value_with_context(field_value, context)
667 })?;
668 json_object.insert(field.name().to_string(), json_value);
669 }
670
671 Ok(Json::Object(json_object))
672}
673
674fn decode_list_with_context(list_value: ListValue, context: &mut JsonContext) -> Result<Json> {
676 let mut json_array = Vec::with_capacity(list_value.len());
677
678 let data_items = list_value.take_items();
679
680 for (index, item) in data_items.into_iter().enumerate() {
681 let json_value = with_key_context(context, &index.to_string(), |context| {
682 decode_value_with_context(item, context)
683 })?;
684 json_array.push(json_value);
685 }
686
687 Ok(Json::Array(json_array))
688}
689
690fn decode_primitive_value(value: Value) -> Result<Json> {
692 match value {
693 Value::Null => Ok(Json::Null),
694 Value::Boolean(b) => Ok(Json::Bool(b)),
695 Value::UInt8(v) => Ok(Json::from(v)),
696 Value::UInt16(v) => Ok(Json::from(v)),
697 Value::UInt32(v) => Ok(Json::from(v)),
698 Value::UInt64(v) => Ok(Json::from(v)),
699 Value::Int8(v) => Ok(Json::from(v)),
700 Value::Int16(v) => Ok(Json::from(v)),
701 Value::Int32(v) => Ok(Json::from(v)),
702 Value::Int64(v) => Ok(Json::from(v)),
703 Value::Float32(v) => Ok(Json::from(v.0)),
704 Value::Float64(v) => Ok(Json::from(v.0)),
705 Value::String(s) => Ok(Json::String(s.as_utf8().to_string())),
706 Value::Binary(b) => serde_json::to_value(b).context(error::SerializeSnafu),
707 Value::Date(v) => Ok(Json::from(v.val())),
708 Value::Timestamp(v) => serde_json::to_value(v.value()).context(error::SerializeSnafu),
709 Value::Time(v) => serde_json::to_value(v.value()).context(error::SerializeSnafu),
710 Value::IntervalYearMonth(v) => {
711 serde_json::to_value(v.to_i32()).context(error::SerializeSnafu)
712 }
713 Value::IntervalDayTime(v) => {
714 serde_json::to_value(v.to_i64()).context(error::SerializeSnafu)
715 }
716 Value::IntervalMonthDayNano(v) => {
717 serde_json::to_value(v.to_i128()).context(error::SerializeSnafu)
718 }
719 Value::Duration(v) => serde_json::to_value(v.value()).context(error::SerializeSnafu),
720 Value::Decimal128(v) => serde_json::to_value(v.to_string()).context(error::SerializeSnafu),
721 Value::Struct(_) | Value::List(_) | Value::Json(_) => {
722 Err(error::InvalidJsonSnafu {
724 value: "Structured values should be handled by context-aware decoding".to_string(),
725 }
726 .build())
727 }
728 }
729}
730
731#[cfg(test)]
732mod tests {
733 use std::sync::Arc;
734
735 use serde_json::json;
736
737 use super::*;
738 use crate::data_type::ConcreteDataType;
739 use crate::types::{ListType, StructField, StructType};
740
741 fn struct_field_value<'a>(struct_value: &'a StructValue, field_name: &str) -> &'a Value {
742 let index = struct_value
743 .struct_type()
744 .fields()
745 .iter()
746 .position(|field| field.name() == field_name)
747 .expect("field exists");
748 &struct_value.items()[index]
749 }
750
751 #[test]
752 fn test_skipping_index_settings_roundtrip() {
753 let old = r#"{"type_hints":[{"path":["service","name"],"type":{"String":{}},"inverted_index":true}]}"#;
754 let legacy: JsonSettings = serde_json::from_str(old).unwrap();
755 assert!(legacy.type_hints()[0].skipping_index.is_none());
756 let mut hints = legacy.type_hints().to_vec();
757 hints[0].skipping_index = Some(crate::schema::SkippingIndexOptions::default());
758 let settings = JsonSettings::try_new(hints.clone(), Some(0)).unwrap();
759 let json = serde_json::to_string(&settings).unwrap();
760 assert_eq!(
761 settings,
762 serde_json::from_str::<JsonSettings>(&json).unwrap()
763 );
764 assert!(!settings.equivalent(&legacy));
765 hints[0].skipping_index.as_mut().unwrap().granularity = 0;
766 assert!(JsonSettings::try_new(hints, Some(0)).is_err());
767 }
768
769 #[test]
770 fn test_json_settings_equivalent() -> Result<()> {
771 let hints = vec![
772 JsonTypeHint {
773 path: vec!["user".to_string(), "name".to_string()],
774 data_type: ConcreteDataType::string_datatype(),
775 inverted_index: false,
776 skipping_index: None,
777 },
778 JsonTypeHint {
779 path: vec!["count".to_string()],
780 data_type: ConcreteDataType::int64_datatype(),
781 inverted_index: false,
782 skipping_index: None,
783 },
784 ];
785 let settings = JsonSettings::try_new(hints.clone(), Some(10))?;
786 let mut reversed = hints.clone();
787 reversed.reverse();
788 let reordered = JsonSettings::try_new(reversed, Some(10))?;
789 assert_ne!(settings, reordered);
790 assert!(settings.equivalent(&reordered));
791 assert!(reordered.equivalent(&settings));
792 assert!(settings.equivalent(&settings));
793 assert_eq!(hints, settings.type_hints());
794 assert!(JsonSettings::default().equivalent(&JsonSettings::default()));
795
796 for limit in [None, Some(0), Some(11)] {
797 assert!(!settings.equivalent(&JsonSettings::try_new(hints.clone(), limit)?));
798 }
799 assert!(!settings.equivalent(&JsonSettings::try_new(vec![hints[0].clone()], Some(10))?));
800
801 let mut changed = hints.clone();
802 changed[0].path = vec!["user".to_string(), "id".to_string()];
803 assert!(!settings.equivalent(&JsonSettings::try_new(changed, Some(10))?));
804
805 let mut changed = hints.clone();
806 changed[0].data_type = ConcreteDataType::int64_datatype();
807 assert!(!settings.equivalent(&JsonSettings::try_new(changed, Some(10))?));
808
809 let mut changed = hints;
810 changed[0].inverted_index = true;
811 assert!(!settings.equivalent(&JsonSettings::try_new(changed, Some(10))?));
812 Ok(())
813 }
814
815 #[test]
816 fn test_json_settings_deserializes_legacy_type_hint_constraints()
817 -> std::result::Result<(), Box<dyn std::error::Error>> {
818 let json_str = r#"{
819 "type_hints": [
820 {
821 "path": ["user", "age"],
822 "type": {
823 "Int64": {}
824 },
825 "nullable": false,
826 "default_constraint": {
827 "Value": {
828 "Int64": 18
829 }
830 },
831 "inverted_index": true
832 },
833 {
834 "path": ["user", "name"],
835 "type": {
836 "String": {
837 "size_type": "Utf8"
838 }
839 },
840 "nullable": true,
841 "inverted_index": false
842 }
843 ]
844 }"#;
845
846 let deserialized = serde_json::from_str::<JsonSettings>(json_str)?;
847
848 let expected = JsonSettings::try_new(
849 vec![
850 JsonTypeHint {
851 path: vec!["user".to_string(), "age".to_string()],
852 data_type: ConcreteDataType::int64_datatype(),
853 inverted_index: true,
854 skipping_index: None,
855 },
856 JsonTypeHint {
857 path: vec!["user".to_string(), "name".to_string()],
858 data_type: ConcreteDataType::string_datatype(),
859 inverted_index: false,
860 skipping_index: None,
861 },
862 ],
863 None,
864 )?;
865 assert_eq!(deserialized, expected);
866 Ok(())
867 }
868
869 #[test]
870 fn test_json_settings_ser_de() -> std::result::Result<(), Box<dyn std::error::Error>> {
871 let type_hints = vec![
872 JsonTypeHint {
873 path: vec!["user".to_string(), "age".to_string()],
874 data_type: ConcreteDataType::int64_datatype(),
875 inverted_index: true,
876 skipping_index: None,
877 },
878 JsonTypeHint {
879 path: vec!["user".to_string(), "name".to_string()],
880 data_type: ConcreteDataType::string_datatype(),
881 inverted_index: false,
882 skipping_index: None,
883 },
884 ];
885
886 for settings in [
887 JsonSettings {
888 type_hints: type_hints.clone(),
889 max_auto_expanded_paths: None,
890 },
891 JsonSettings {
892 type_hints,
893 max_auto_expanded_paths: Some(1),
894 },
895 ] {
896 let serialized = serde_json::to_string(&settings)?;
897 let deserialized = serde_json::from_str::<JsonSettings>(&serialized)?;
898 assert_eq!(settings, deserialized);
899 }
900 Ok(())
901 }
902
903 #[test]
904 fn test_json_settings_path_validation() {
905 for (path, expected_valid) in [
906 (vec![""], false),
907 (vec!["", "nested"], false),
908 (vec!["nested", ""], false),
909 (vec!["nested", "", "leaf"], false),
910 (vec!["nested", JSON2_REMAINDER_FIELD_NAME], false),
911 (vec!["nested", JSON2_REMAINDER_FIELD_NAME, "leaf"], false),
912 (vec!["nested", "leaf"], true),
913 (vec![], false),
914 (vec![JSON2_REMAINDER_FIELD_NAME], false),
915 (vec![JSON2_REMAINDER_FIELD_NAME, "leaf"], false),
916 ] {
917 for inverted_index in [false, true] {
918 let hint = JsonTypeHint {
919 path: path.iter().map(|part| (*part).to_string()).collect(),
920 data_type: ConcreteDataType::string_datatype(),
921 inverted_index,
922 skipping_index: None,
923 };
924 let serialized = json!({"type_hints": [hint.clone()]});
925 assert_eq!(
926 JsonSettings::try_new(vec![hint], None).is_ok(),
927 expected_valid,
928 "path: {path:?}, inverted_index: {inverted_index}"
929 );
930 assert_eq!(
931 serde_json::from_value::<JsonSettings>(serialized).is_ok(),
932 expected_valid,
933 "path: {path:?}, inverted_index: {inverted_index}"
934 );
935 }
936 }
937 }
938
939 #[test]
940 fn test_json_settings_reject_invalid_type_hints() {
941 for type_hints in [
942 json!([{"path": [], "type": {"Int64": {}}, "nullable": true, "inverted_index": false}]),
943 json!([
944 {"path": ["a"], "type": {"Int64": {}}, "nullable": true, "inverted_index": false},
945 {"path": ["a", "b"], "type": {"Int64": {}}, "nullable": true, "inverted_index": false}
946 ]),
947 json!([{"path": [JSON2_REMAINDER_FIELD_NAME], "type": {"Int64": {}}, "nullable": true, "inverted_index": false}]),
948 ] {
949 let settings = json!({"type_hints": type_hints});
950 assert!(serde_json::from_value::<JsonSettings>(settings).is_err());
951 }
952
953 let hint = |path, data_type| JsonTypeHint {
954 path,
955 data_type,
956 inverted_index: false,
957 skipping_index: None,
958 };
959 assert!(
960 JsonSettings::try_new(
961 vec![hint(
962 vec!["nested".to_string(), JSON2_REMAINDER_FIELD_NAME.to_string()],
963 ConcreteDataType::string_datatype(),
964 )],
965 None,
966 )
967 .is_err()
968 );
969 assert!(
970 JsonSettings::try_new(
971 vec![hint(
972 vec!["nested".to_string(); JSON2_MAX_STRUCTURED_DEPTH + 1],
973 ConcreteDataType::string_datatype(),
974 )],
975 None,
976 )
977 .is_err()
978 );
979 assert!(
980 JsonSettings::try_new(
981 vec![hint(
982 vec!["date".to_string()],
983 ConcreteDataType::date_datatype(),
984 )],
985 None,
986 )
987 .is_err()
988 );
989 }
990
991 #[test]
992 fn test_encode_root_non_object_json() {
993 let settings = JsonSettings::default();
994 let cases = [
995 ("null", Json::Null),
996 ("boolean", Json::Bool(true)),
997 ("integer", Json::from(42)),
998 ("float", Json::from(3.15)),
999 ("string", Json::String("hello".to_string())),
1000 ("array", json!([1, 2, 3])),
1001 ("mixed array", json!([1, "hello", true, 3.15])),
1002 ("empty array", json!([])),
1003 ];
1004
1005 for (name, json) in cases {
1006 let err = settings.encode(json).unwrap_err();
1007 assert!(
1008 matches!(err, crate::error::Error::UnsupportedJsonType { .. }),
1009 "{name}: {err:?}"
1010 );
1011 }
1012 }
1013
1014 #[test]
1015 fn test_coerce_or_null_on_type_hint_mismatch() -> Result<()> {
1016 let settings = JsonSettings::try_new(
1017 vec![JsonTypeHint {
1018 path: vec!["kind".to_string()],
1019 data_type: ConcreteDataType::int64_datatype(),
1020 inverted_index: false,
1021 skipping_index: None,
1022 }],
1023 None,
1024 )?;
1025
1026 let result = settings
1027 .encode_with_type_hint_mismatch_policy(
1028 json!({"kind": "invalid", "message": "kept"}),
1029 TypeHintMismatchPolicy::CoerceOrNull,
1030 )?
1031 .into_json_inner()
1032 .unwrap();
1033 let Value::Struct(root) = result else {
1034 panic!("Expected Struct value");
1035 };
1036 assert_eq!(struct_field_value(&root, "kind"), &Value::Null);
1037 assert_eq!(
1038 struct_field_value(&root, "message"),
1039 &Value::String("kept".into())
1040 );
1041 Ok(())
1042 }
1043
1044 #[test]
1045 fn test_coerce_or_null_converts_type_hint_values() -> Result<()> {
1046 let settings = JsonSettings::try_new(
1047 vec![
1048 JsonTypeHint {
1049 path: vec!["to_string".to_string()],
1050 data_type: ConcreteDataType::string_datatype(),
1051 inverted_index: false,
1052 skipping_index: None,
1053 },
1054 JsonTypeHint {
1055 path: vec!["to_int".to_string()],
1056 data_type: ConcreteDataType::int64_datatype(),
1057 inverted_index: false,
1058 skipping_index: None,
1059 },
1060 ],
1061 None,
1062 )?;
1063
1064 let result = settings
1065 .encode_with_type_hint_mismatch_policy(
1066 json!({"to_string": 123, "to_int": "456", "invalid": "value"}),
1067 TypeHintMismatchPolicy::CoerceOrNull,
1068 )?
1069 .into_json_inner()
1070 .unwrap();
1071 let Value::Struct(root) = result else {
1072 panic!("Expected Struct value");
1073 };
1074 assert_eq!(
1075 struct_field_value(&root, "to_string"),
1076 &Value::String("123".into())
1077 );
1078 assert_eq!(struct_field_value(&root, "to_int"), &Value::Int64(456));
1079 assert_eq!(
1080 struct_field_value(&root, "invalid"),
1081 &Value::String("value".into())
1082 );
1083 Ok(())
1084 }
1085
1086 #[test]
1087 fn test_coerce_or_null_on_nested_type_hint_structure_mismatch() -> Result<()> {
1088 let settings = JsonSettings::try_new(
1089 vec![JsonTypeHint {
1090 path: vec!["user".to_string(), "age".to_string()],
1091 data_type: ConcreteDataType::int64_datatype(),
1092 inverted_index: false,
1093 skipping_index: None,
1094 }],
1095 None,
1096 )?;
1097
1098 let result = settings
1099 .encode_with_type_hint_mismatch_policy(
1100 json!({"user": "invalid", "message": "kept"}),
1101 TypeHintMismatchPolicy::CoerceOrNull,
1102 )?
1103 .into_json_inner()
1104 .unwrap();
1105 let Value::Struct(root) = result else {
1106 panic!("Expected Struct value");
1107 };
1108 assert_eq!(struct_field_value(&root, "user"), &Value::Null);
1109 assert_eq!(
1110 struct_field_value(&root, "message"),
1111 &Value::String("kept".into())
1112 );
1113 Ok(())
1114 }
1115
1116 #[test]
1117 fn test_encode_rejects_reserved_remainder_field() -> Result<()> {
1118 let settings = JsonSettings::default();
1119 let err = settings
1120 .encode(json!({"!__remainder__!": "user-value"}))
1121 .unwrap_err();
1122 assert!(
1123 err.to_string()
1124 .contains("root object cannot contain reserved field")
1125 );
1126
1127 settings.encode(json!({"nested": {"!__remainder__!": "user-value"}}))?;
1128 Ok(())
1129 }
1130
1131 #[test]
1132 fn test_encode_json_object() {
1133 let json = json!({
1134 "name": "John",
1135 "age": 30,
1136 "active": true
1137 });
1138
1139 let settings = JsonSettings::default();
1140 let result = settings.encode(json).unwrap().into_json_inner().unwrap();
1141 let Value::Struct(result) = result else {
1142 panic!("Expected Struct value");
1143 };
1144 assert_eq!(result.items().len(), 3);
1145
1146 let items = result.items();
1147 let struct_type = result.struct_type();
1148
1149 let fields = struct_type.fields();
1151 let field_names: Vec<&str> = fields.iter().map(|f| f.name()).collect();
1152 assert!(field_names.contains(&"name"));
1153 assert!(field_names.contains(&"age"));
1154 assert!(field_names.contains(&"active"));
1155
1156 for (i, field) in struct_type.fields().iter().enumerate() {
1158 match field.name() {
1159 "name" => {
1160 assert_eq!(items[i], Value::String("John".into()));
1161 assert_eq!(field.data_type(), &ConcreteDataType::string_datatype());
1162 }
1163 "age" => {
1164 assert_eq!(items[i], Value::Int64(30));
1165 assert_eq!(field.data_type(), &ConcreteDataType::int64_datatype());
1166 }
1167 "active" => {
1168 assert_eq!(items[i], Value::Boolean(true));
1169 assert_eq!(field.data_type(), &ConcreteDataType::boolean_datatype());
1170 }
1171 _ => panic!("Unexpected field: {}", field.name()),
1172 }
1173 }
1174 }
1175
1176 #[test]
1177 fn test_encode_json_nested_object() {
1178 let json = json!({
1179 "person": {
1180 "name": "Alice",
1181 "age": 25
1182 },
1183 "scores": [95, 87, 92]
1184 });
1185
1186 let settings = JsonSettings::default();
1187 let result = settings.encode(json).unwrap().into_json_inner().unwrap();
1188 let Value::Struct(result) = result else {
1189 panic!("Expected Struct value");
1190 };
1191 assert_eq!(result.items().len(), 2);
1192
1193 let items = result.items();
1194 let struct_type = result.struct_type();
1195
1196 let person_index = struct_type
1198 .fields()
1199 .iter()
1200 .position(|f| f.name() == "person")
1201 .unwrap();
1202 if let Value::Struct(person_struct) = &items[person_index] {
1203 assert_eq!(person_struct.items().len(), 2);
1204 let fields = person_struct.struct_type().fields();
1205 let person_fields: Vec<&str> = fields.iter().map(|f| f.name()).collect();
1206 assert!(person_fields.contains(&"name"));
1207 assert!(person_fields.contains(&"age"));
1208 } else {
1209 panic!("Expected Struct value for person field");
1210 }
1211
1212 let scores_index = struct_type
1214 .fields()
1215 .iter()
1216 .position(|f| f.name() == "scores")
1217 .unwrap();
1218 if let Value::List(scores_list) = &items[scores_index] {
1219 assert_eq!(scores_list.items().len(), 3);
1220 assert_eq!(scores_list.items()[0], Value::Int64(95));
1221 assert_eq!(scores_list.items()[1], Value::Int64(87));
1222 assert_eq!(scores_list.items()[2], Value::Int64(92));
1223 } else {
1224 panic!("Expected List value for scores field");
1225 }
1226 }
1227
1228 #[test]
1229 fn test_encode_json_structured() {
1230 let json = json!({
1231 "name": "Bob",
1232 "age": 35
1233 });
1234
1235 let settings = JsonSettings::default();
1236 let result = settings.encode(json).unwrap().into_json_inner().unwrap();
1237
1238 if let Value::Struct(struct_value) = result {
1239 assert_eq!(struct_value.items().len(), 2);
1240 let fields = struct_value.struct_type().fields();
1241 let field_names: Vec<&str> = fields.iter().map(|f| f.name()).collect();
1242 assert!(field_names.contains(&"name"));
1243 assert!(field_names.contains(&"age"));
1244 } else {
1245 panic!("Expected Struct value");
1246 }
1247 }
1248
1249 #[test]
1250 fn test_encode_json_respects_type_hint() -> Result<()> {
1251 let type_hints = vec![JsonTypeHint {
1252 path: vec!["age".to_string()],
1253 data_type: ConcreteDataType::int64_datatype(),
1254 inverted_index: false,
1255 skipping_index: None,
1256 }];
1257 let settings = JsonSettings::try_new(type_hints, None)?;
1258
1259 let result = settings
1260 .encode(json!({
1261 "name": "Alice",
1262 "age": 42
1263 }))
1264 .unwrap()
1265 .into_json_inner()
1266 .unwrap();
1267
1268 let Value::Struct(struct_value) = result else {
1269 panic!("Expected Struct value");
1270 };
1271 assert_eq!(struct_field_value(&struct_value, "age"), &Value::Int64(42));
1272
1273 let err = settings
1274 .encode(json!({
1275 "age": "42"
1276 }))
1277 .unwrap_err();
1278 assert!(err.to_string().contains("does not match JSON2 type hint"));
1279 Ok(())
1280 }
1281
1282 #[test]
1283 fn test_encode_json_respects_unsigned_type_hint() -> Result<()> {
1284 let type_hints = vec![JsonTypeHint {
1285 path: vec!["count".to_string()],
1286 data_type: ConcreteDataType::uint64_datatype(),
1287 inverted_index: false,
1288 skipping_index: None,
1289 }];
1290 let settings = JsonSettings::try_new(type_hints, None)?;
1291
1292 let result = settings
1293 .encode(json!({
1294 "count": u64::MAX
1295 }))
1296 .unwrap()
1297 .into_json_inner()
1298 .unwrap();
1299
1300 let Value::Struct(struct_value) = result else {
1301 panic!("Expected Struct value");
1302 };
1303 assert_eq!(
1304 struct_field_value(&struct_value, "count"),
1305 &Value::UInt64(u64::MAX)
1306 );
1307
1308 let err = settings
1309 .encode(json!({
1310 "count": -1
1311 }))
1312 .unwrap_err();
1313 assert!(err.to_string().contains("does not match JSON2 type hint"));
1314 Ok(())
1315 }
1316
1317 #[test]
1318 fn test_encode_json_fills_missing_type_hint_with_null() -> Result<()> {
1319 let type_hints = vec![JsonTypeHint {
1320 path: vec!["user".to_string(), "age".to_string()],
1321 data_type: ConcreteDataType::int64_datatype(),
1322 inverted_index: false,
1323 skipping_index: None,
1324 }];
1325 let settings = JsonSettings::try_new(type_hints, None)?;
1326
1327 let result = settings
1328 .encode(json!({}))
1329 .unwrap()
1330 .into_json_inner()
1331 .unwrap();
1332
1333 let Value::Struct(root) = result else {
1334 panic!("Expected Struct value");
1335 };
1336 let Value::Struct(user) = struct_field_value(&root, "user") else {
1337 panic!("Expected user Struct value");
1338 };
1339 assert_eq!(struct_field_value(user, "age"), &Value::Null);
1340 Ok(())
1341 }
1342
1343 #[test]
1344 fn test_encode_json_fills_missing_nullable_type_hint_with_null() -> Result<()> {
1345 let type_hints = vec![JsonTypeHint {
1346 path: vec!["user".to_string(), "name".to_string()],
1347 data_type: ConcreteDataType::string_datatype(),
1348 inverted_index: false,
1349 skipping_index: None,
1350 }];
1351 let settings = JsonSettings::try_new(type_hints, None)?;
1352
1353 let result = settings
1354 .encode(json!({ "user": {} }))
1355 .unwrap()
1356 .into_json_inner()
1357 .unwrap();
1358
1359 let Value::Struct(root) = result else {
1360 panic!("Expected Struct value");
1361 };
1362 let Value::Struct(user) = struct_field_value(&root, "user") else {
1363 panic!("Expected user Struct value");
1364 };
1365 assert_eq!(struct_field_value(user, "name"), &Value::Null);
1366 Ok(())
1367 }
1368
1369 #[test]
1370 fn test_encode_json_allows_missing_type_hint() -> Result<()> {
1371 let type_hints = vec![JsonTypeHint {
1372 path: vec!["user".to_string(), "age".to_string()],
1373 data_type: ConcreteDataType::int64_datatype(),
1374 inverted_index: false,
1375 skipping_index: None,
1376 }];
1377 let settings = JsonSettings::try_new(type_hints, None)?;
1378
1379 settings.encode(json!({}))?;
1380 Ok(())
1381 }
1382
1383 #[test]
1384 fn test_encode_json_merges_missing_type_hint_prefix() -> Result<()> {
1385 let type_hints = vec![
1386 JsonTypeHint {
1387 path: vec!["user".to_string(), "age".to_string()],
1388 data_type: ConcreteDataType::int64_datatype(),
1389 inverted_index: false,
1390 skipping_index: None,
1391 },
1392 JsonTypeHint {
1393 path: vec!["user".to_string(), "name".to_string()],
1394 data_type: ConcreteDataType::string_datatype(),
1395 inverted_index: false,
1396 skipping_index: None,
1397 },
1398 ];
1399 let settings = JsonSettings::try_new(type_hints, None)?;
1400
1401 let result = settings.encode(json!({}))?.into_json_inner().unwrap();
1402
1403 let Value::Struct(root) = result else {
1404 panic!("Expected Struct value");
1405 };
1406 let Value::Struct(user) = struct_field_value(&root, "user") else {
1407 panic!("Expected user Struct value");
1408 };
1409 assert_eq!(struct_field_value(user, "age"), &Value::Null);
1410 assert_eq!(struct_field_value(user, "name"), &Value::Null);
1411 Ok(())
1412 }
1413
1414 #[test]
1415 fn test_json_settings_structured() {
1416 let json = json!({
1417 "name": "Eve",
1418 "score": 95
1419 });
1420
1421 let settings = JsonSettings::default();
1422 let result = settings.encode(json).unwrap().into_json_inner().unwrap();
1423
1424 if let Value::Struct(struct_value) = result {
1425 assert_eq!(struct_value.items().len(), 2);
1426 } else {
1427 panic!("Expected Struct value");
1428 }
1429 }
1430
1431 #[cfg(test)]
1432 mod decode_tests {
1433 use ordered_float::OrderedFloat;
1434 use serde_json::json;
1435
1436 use super::*;
1437
1438 #[test]
1439 fn test_decode_primitive_values() {
1440 let settings = JsonSettings::default();
1441
1442 let result = settings.decode(Value::Null).unwrap();
1444 assert_eq!(result, Json::Null);
1445
1446 let result = settings.decode(Value::Boolean(true)).unwrap();
1448 assert_eq!(result, Json::Bool(true));
1449
1450 let result = settings.decode(Value::Int64(42)).unwrap();
1452 assert_eq!(result, Json::from(42));
1453
1454 let result = settings.decode(Value::Float64(OrderedFloat(3.16))).unwrap();
1456 assert_eq!(result, Json::from(3.16));
1457
1458 let result = settings.decode(Value::String("hello".into())).unwrap();
1460 assert_eq!(result, Json::String("hello".to_string()));
1461 }
1462
1463 #[test]
1464 fn test_decode_struct() {
1465 let settings = JsonSettings::default();
1466
1467 let struct_value = StructValue::new(
1468 vec![
1469 Value::String("Alice".into()),
1470 Value::Int64(25),
1471 Value::Boolean(true),
1472 ],
1473 StructType::new(Arc::new(vec![
1474 StructField::new(
1475 "name".to_string(),
1476 ConcreteDataType::string_datatype(),
1477 true,
1478 ),
1479 StructField::new("age".to_string(), ConcreteDataType::int64_datatype(), true),
1480 StructField::new(
1481 "active".to_string(),
1482 ConcreteDataType::boolean_datatype(),
1483 true,
1484 ),
1485 ])),
1486 );
1487
1488 let result = settings.decode(Value::Struct(struct_value)).unwrap();
1489 let expected = json!({
1490 "name": "Alice",
1491 "age": 25,
1492 "active": true
1493 });
1494 assert_eq!(result, expected);
1495 }
1496
1497 #[test]
1498 fn test_decode_list() {
1499 let settings = JsonSettings::default();
1500
1501 let list_value = ListValue::new(
1502 vec![Value::Int64(1), Value::Int64(2), Value::Int64(3)],
1503 Arc::new(ConcreteDataType::int64_datatype()),
1504 );
1505
1506 let result = settings.decode(Value::List(list_value)).unwrap();
1507 let expected = json!([1, 2, 3]);
1508 assert_eq!(result, expected);
1509 }
1510
1511 #[test]
1512 fn test_decode_nested_structure() {
1513 let settings = JsonSettings::default();
1514
1515 let inner_struct = StructValue::new(
1516 vec![Value::String("Alice".into()), Value::Int64(25)],
1517 StructType::new(Arc::new(vec![
1518 StructField::new(
1519 "name".to_string(),
1520 ConcreteDataType::string_datatype(),
1521 true,
1522 ),
1523 StructField::new("age".to_string(), ConcreteDataType::int64_datatype(), true),
1524 ])),
1525 );
1526
1527 let score_list_item_type = Arc::new(ConcreteDataType::int64_datatype());
1528 let outer_struct = StructValue::new(
1529 vec![
1530 Value::Struct(inner_struct),
1531 Value::List(ListValue::new(
1532 vec![Value::Int64(95), Value::Int64(87)],
1533 score_list_item_type.clone(),
1534 )),
1535 ],
1536 StructType::new(Arc::new(vec![
1537 StructField::new(
1538 "user".to_string(),
1539 ConcreteDataType::Struct(StructType::new(Arc::new(vec![
1540 StructField::new(
1541 "name".to_string(),
1542 ConcreteDataType::string_datatype(),
1543 true,
1544 ),
1545 StructField::new(
1546 "age".to_string(),
1547 ConcreteDataType::int64_datatype(),
1548 true,
1549 ),
1550 ]))),
1551 true,
1552 ),
1553 StructField::new(
1554 "scores".to_string(),
1555 ConcreteDataType::List(ListType::new(score_list_item_type.clone())),
1556 true,
1557 ),
1558 ])),
1559 );
1560
1561 let result = settings.decode(Value::Struct(outer_struct)).unwrap();
1562 let expected = json!({
1563 "user": {
1564 "name": "Alice",
1565 "age": 25
1566 },
1567 "scores": [95, 87]
1568 });
1569 assert_eq!(result, expected);
1570 }
1571
1572 #[test]
1573 fn test_decode_missing_fields() {
1574 let settings = JsonSettings::default();
1575
1576 let struct_value = StructValue::new(
1578 vec![
1579 Value::String("Bob".into()),
1580 Value::Null, ],
1582 StructType::new(Arc::new(vec![
1583 StructField::new(
1584 "name".to_string(),
1585 ConcreteDataType::string_datatype(),
1586 true,
1587 ),
1588 StructField::new("age".to_string(), ConcreteDataType::int64_datatype(), true),
1589 ])),
1590 );
1591
1592 let result = settings.decode(Value::Struct(struct_value)).unwrap();
1593 let expected = json!({
1594 "name": "Bob",
1595 "age": null
1596 });
1597 assert_eq!(result, expected);
1598 }
1599 }
1600}