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