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