Skip to main content

datatypes/
json.rs

1// Copyright 2023 Greptime Team
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15//! Data conversion between greptime's StructType and Json
16//!
17//! The idea of this module is to provide utilities to convert serde_json::Value to greptime's StructType and vice versa.
18//!
19//! The struct will carry all the fields of the Json object. We will not flatten any json object in this implementation.
20//!
21
22pub 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
39/// Maximum number of JSON container levels represented as nested Arrow types.
40pub const JSON2_MAX_STRUCTURED_DEPTH: usize = 50;
41/// Reserved physical field containing unexpanded JSON2 paths.
42pub const JSON2_REMAINDER_FIELD_NAME: &str = "!__remainder__!";
43/// Default maximum number of unhinted JSON leaf paths expanded into Arrow fields.
44pub const JSON2_DEFAULT_MAX_AUTO_EXPANDED_PATHS: u32 = 100;
45
46/// JSON2 settings stored in column schema metadata and represented through
47/// Arrow extension metadata.
48#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize)]
49pub struct JsonSettings {
50    #[serde(default)]
51    type_hints: Vec<JsonTypeHint>,
52    /// Maximum number of unhinted JSON paths expanded into Arrow fields.
53    ///
54    /// `None` preserves the legacy unlimited layout.
55    #[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/// Declares selected JSON2 subpaths as typed fields.
79///
80/// These hints let JSON2 encode frequently used subpaths in a typed layout, so
81/// queries over those subpaths can get behavior and performance closer to
82/// ordinary columns.
83#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
84pub struct JsonTypeHint {
85    /// JSON2 subpath for a typed field.
86    ///
87    /// Each item is one JSON object key. For example, `["user", "age"]`
88    /// represents `user.age`.
89    ///
90    /// Array traversal is not currently supported. For example, a hint cannot
91    /// describe `events[0].name` or fields shared by all items in `events[*]`.
92    pub path: Vec<String>,
93    #[serde(rename = "type")]
94    pub data_type: ConcreteDataType,
95    pub inverted_index: bool,
96}
97
98/// Specifies how encoding handles a value that does not match its JSON2 type hint.
99#[derive(Debug, Clone, Copy, PartialEq, Eq)]
100pub enum TypeHintMismatchPolicy {
101    /// Returns an error for a type hint mismatch.
102    Reject,
103    /// Coerces to the hint type, encoding unconvertible values as null.
104    CoerceOrNull,
105}
106
107/// Context for JSON encoding/decoding that tracks the current key path.
108#[derive(Clone, Debug)]
109pub struct JsonContext<'a> {
110    /// Current key path from the JSON2 root.
111    pub path: Vec<String>,
112    /// Settings for JSON encoding/decoding.
113    pub settings: &'a JsonSettings,
114}
115
116impl JsonSettings {
117    /// Creates default v2 settings for newly created JSON2 columns.
118    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    /// Creates and validates JSON2 settings.
126    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    /// Returns whether the settings are equivalent, ignoring type hint order.
150    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    /// Decode an encoded StructValue back into a serde_json::Value.
174    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    /// Encode a serde_json::Value into a Value::Json using current settings.
183    pub fn encode(&self, json: Json) -> Result<Value> {
184        self.encode_with_type_hint_mismatch_policy(json, TypeHintMismatchPolicy::Reject)
185    }
186
187    /// Encodes a serde_json::Value using the given type hint mismatch policy.
188    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
313/// Main encoding function with key path tracking
314fn 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
486/// Coerces a JSON value using the same semantics as JSON2 query projection.
487/// Values that cannot be converted become null.
488pub(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    // In specification, it's valid for a JSON array to have different types of items, for example,
569    // ["a string", 1]. However, in implementation, the `JsonValue` will be converted to Arrow list
570    // array, which requires all items have exactly the same type. So we merge out the maybe
571    // different item types to a unified type, and align all the item values to it.
572
573    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
597/// Helper function to encode a JSON value to a Value and determine its ConcreteDataType with context
598fn 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                // Fallback to string representation
627                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
636/// Main decoding function with key path tracking
637fn 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
645/// Decode a structured value to JSON object
646fn 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
664/// Decode a list value to JSON array
665fn 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
680/// Decode primitive value to JSON
681fn 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            // These should be handled by the context-aware functions
713            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        // Check that we have the expected fields
1110        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        // Find and check each field
1117        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        // Check person field (nested struct)
1157        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        // Check scores field (list)
1173        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            // Test null
1396            let result = settings.decode(Value::Null).unwrap();
1397            assert_eq!(result, Json::Null);
1398
1399            // Test boolean
1400            let result = settings.decode(Value::Boolean(true)).unwrap();
1401            assert_eq!(result, Json::Bool(true));
1402
1403            // Test integer
1404            let result = settings.decode(Value::Int64(42)).unwrap();
1405            assert_eq!(result, Json::from(42));
1406
1407            // Test float
1408            let result = settings.decode(Value::Float64(OrderedFloat(3.16))).unwrap();
1409            assert_eq!(result, Json::from(3.16));
1410
1411            // Test string
1412            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            // Struct with missing field (null value)
1530            let struct_value = StructValue::new(
1531                vec![
1532                    Value::String("Bob".into()),
1533                    Value::Null, // missing age field
1534                ],
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}