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        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
309/// Main encoding function with key path tracking
310fn 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
482/// Coerces a JSON value using the same semantics as JSON2 query projection.
483/// Values that cannot be converted become null.
484pub(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    // In specification, it's valid for a JSON array to have different types of items, for example,
565    // ["a string", 1]. However, in implementation, the `JsonValue` will be converted to Arrow list
566    // array, which requires all items have exactly the same type. So we merge out the maybe
567    // different item types to a unified type, and align all the item values to it.
568
569    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
593/// Helper function to encode a JSON value to a Value and determine its ConcreteDataType with context
594fn 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                // Fallback to string representation
623                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
632/// Main decoding function with key path tracking
633fn 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
641/// Decode a structured value to JSON object
642fn 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
660/// Decode a list value to JSON array
661fn 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
676/// Decode primitive value to JSON
677fn 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            // These should be handled by the context-aware functions
709            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        // Check that we have the expected fields
1071        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        // Find and check each field
1078        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        // Check person field (nested struct)
1118        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        // Check scores field (list)
1134        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            // Test null
1357            let result = settings.decode(Value::Null).unwrap();
1358            assert_eq!(result, Json::Null);
1359
1360            // Test boolean
1361            let result = settings.decode(Value::Boolean(true)).unwrap();
1362            assert_eq!(result, Json::Bool(true));
1363
1364            // Test integer
1365            let result = settings.decode(Value::Int64(42)).unwrap();
1366            assert_eq!(result, Json::from(42));
1367
1368            // Test float
1369            let result = settings.decode(Value::Float64(OrderedFloat(3.16))).unwrap();
1370            assert_eq!(result, Json::from(3.16));
1371
1372            // Test string
1373            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            // Struct with missing field (null value)
1491            let struct_value = StructValue::new(
1492                vec![
1493                    Value::String("Bob".into()),
1494                    Value::Null, // missing age field
1495                ],
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}