Skip to main content

datatypes/types/
json_type.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
15use std::collections::BTreeMap;
16use std::fmt::{Debug, Display, Formatter};
17use std::str::FromStr;
18use std::sync::{Arc, LazyLock};
19
20use arrow::datatypes::DataType as ArrowDataType;
21use arrow_schema::{Field, Fields};
22use common_base::bytes::Bytes;
23use regex::{Captures, Regex};
24use serde::{Deserialize, Serialize};
25use snafu::ResultExt;
26
27use crate::Error;
28use crate::data_type::DataType;
29use crate::error::{
30    DeserializeSnafu, InvalidJsonSnafu, InvalidJsonbSnafu, Result, UnsupportedArrowTypeSnafu,
31};
32use crate::prelude::ConcreteDataType;
33use crate::scalars::ScalarVectorBuilder;
34use crate::type_id::LogicalTypeId;
35use crate::value::Value;
36use crate::vectors::json::builder::JsonVectorBuilder;
37use crate::vectors::{BinaryVectorBuilder, MutableVector};
38
39pub const JSON_TYPE_NAME: &str = "Json";
40const JSON2_TYPE_NAME: &str = "Json2";
41
42pub type JsonObjectType = BTreeMap<String, JsonNativeType>;
43
44#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize)]
45pub enum JsonNumberType {
46    U64,
47    I64,
48    F64,
49}
50
51#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize, Default)]
52pub enum JsonNativeType {
53    /// JSON null value type.
54    ///
55    /// This variant may also appear as the initial state while merging inferred
56    /// types, but it does not represent an empty object. Empty objects are
57    /// represented as `Object({})`.
58    #[default]
59    Null,
60    Bool,
61    Number(JsonNumberType),
62    String,
63    Array(Box<JsonNativeType>),
64    Object(JsonObjectType),
65    /// A special (not in the JSON official specification) JSON type to indicate the "resolved" or
66    /// "lifted" type of two conflicting JSON types. For example, when merging JSON types of "Bool"
67    /// and "Number".
68    Variant,
69}
70
71impl JsonNativeType {
72    pub fn is_null(&self) -> bool {
73        matches!(self, JsonNativeType::Null)
74    }
75
76    pub fn u64() -> Self {
77        Self::Number(JsonNumberType::U64)
78    }
79
80    pub fn i64() -> Self {
81        Self::Number(JsonNumberType::I64)
82    }
83
84    pub fn f64() -> Self {
85        Self::Number(JsonNumberType::F64)
86    }
87
88    pub fn object() -> Self {
89        Self::Object(JsonObjectType::new())
90    }
91
92    /// Merge other [JsonNativeType] into this.
93    /// Conflicting fields will be resolved to the "Variant" type.
94    pub fn merge(&mut self, other: &JsonNativeType) {
95        if self == other {
96            return;
97        }
98
99        fn merge_object(this: &mut JsonObjectType, that: &JsonObjectType) {
100            // merge "that" into "this" directly:
101            for (type_name, that_type) in that {
102                if let Some(this_type) = this.get_mut(type_name) {
103                    this_type.merge(that_type);
104                } else {
105                    this.insert(type_name.clone(), that_type.clone());
106                }
107            }
108        }
109
110        let zelf = std::mem::take(self);
111        *self = match (zelf, other) {
112            (JsonNativeType::Object(mut this), JsonNativeType::Object(that)) => {
113                merge_object(&mut this, that);
114                JsonNativeType::Object(this)
115            }
116            (JsonNativeType::Array(mut this), JsonNativeType::Array(that)) => {
117                this.merge(that);
118                JsonNativeType::Array(this)
119            }
120            (JsonNativeType::Null, that) => that.clone(),
121            (this, JsonNativeType::Null) => this,
122            (this, that) if this == *that => this,
123
124            _ => JsonNativeType::Variant,
125        };
126    }
127
128    pub fn as_arrow_type(&self) -> ArrowDataType {
129        match self {
130            JsonNativeType::Null => ArrowDataType::Null,
131            JsonNativeType::Bool => ArrowDataType::Boolean,
132            JsonNativeType::Number(n) => match n {
133                JsonNumberType::U64 => ArrowDataType::UInt64,
134                JsonNumberType::I64 => ArrowDataType::Int64,
135                JsonNumberType::F64 => ArrowDataType::Float64,
136            },
137            JsonNativeType::String => ArrowDataType::Utf8View,
138            JsonNativeType::Array(array) => {
139                ArrowDataType::List(Arc::new(Field::new("item", array.as_arrow_type(), true)))
140            }
141            JsonNativeType::Object(object) => {
142                let fields = object
143                    .iter()
144                    .map(|(k, v)| Arc::new(Field::new(k, v.as_arrow_type(), true)))
145                    .collect::<Vec<_>>();
146                ArrowDataType::Struct(Fields::from(fields))
147            }
148            JsonNativeType::Variant => ArrowDataType::Binary,
149        }
150    }
151
152    /// Returns whether this type is a boolean, number, or string scalar.
153    pub fn is_primitive(&self) -> bool {
154        matches!(
155            self,
156            JsonNativeType::Bool | JsonNativeType::Number(_) | JsonNativeType::String
157        )
158    }
159}
160
161impl From<&ConcreteDataType> for JsonNativeType {
162    fn from(value: &ConcreteDataType) -> Self {
163        match value {
164            ConcreteDataType::Null(_) => JsonNativeType::Null,
165            ConcreteDataType::Boolean(_) => JsonNativeType::Bool,
166            ConcreteDataType::UInt64(_)
167            | ConcreteDataType::UInt32(_)
168            | ConcreteDataType::UInt16(_)
169            | ConcreteDataType::UInt8(_) => JsonNativeType::u64(),
170            ConcreteDataType::Int64(_)
171            | ConcreteDataType::Int32(_)
172            | ConcreteDataType::Int16(_)
173            | ConcreteDataType::Int8(_) => JsonNativeType::i64(),
174            ConcreteDataType::Float64(_) | ConcreteDataType::Float32(_) => JsonNativeType::f64(),
175            ConcreteDataType::String(_) => JsonNativeType::String,
176            ConcreteDataType::List(list_type) => {
177                JsonNativeType::Array(Box::new(JsonNativeType::from(list_type.item_type())))
178            }
179            ConcreteDataType::Struct(struct_type) => JsonNativeType::Object(
180                struct_type
181                    .fields()
182                    .iter()
183                    .map(|field| (field.name().to_string(), field.data_type().into()))
184                    .collect(),
185            ),
186            ConcreteDataType::Json(json_type) => json_type.native_type().clone(),
187            ConcreteDataType::Binary(_) => JsonNativeType::Variant,
188            _ => unreachable!(),
189        }
190    }
191}
192
193impl TryFrom<&ArrowDataType> for JsonNativeType {
194    type Error = Error;
195
196    fn try_from(t: &ArrowDataType) -> Result<Self> {
197        let t = match t {
198            ArrowDataType::Null => JsonNativeType::Null,
199            ArrowDataType::Boolean => JsonNativeType::Bool,
200            ArrowDataType::Int8
201            | ArrowDataType::Int16
202            | ArrowDataType::Int32
203            | ArrowDataType::Int64 => JsonNativeType::i64(),
204            ArrowDataType::UInt8
205            | ArrowDataType::UInt16
206            | ArrowDataType::UInt32
207            | ArrowDataType::UInt64 => JsonNativeType::u64(),
208            ArrowDataType::Float16 | ArrowDataType::Float32 | ArrowDataType::Float64 => {
209                JsonNativeType::f64()
210            }
211            ArrowDataType::Binary
212            | ArrowDataType::FixedSizeBinary(_)
213            | ArrowDataType::LargeBinary
214            | ArrowDataType::BinaryView => JsonNativeType::Variant,
215            ArrowDataType::Utf8 | ArrowDataType::LargeUtf8 | ArrowDataType::Utf8View => {
216                JsonNativeType::String
217            }
218            ArrowDataType::List(field)
219            | ArrowDataType::ListView(field)
220            | ArrowDataType::FixedSizeList(field, _)
221            | ArrowDataType::LargeList(field)
222            | ArrowDataType::LargeListView(field) => {
223                JsonNativeType::Array(Box::new(Self::try_from(field.data_type())?))
224            }
225            ArrowDataType::Struct(fields) => {
226                let mut object = JsonObjectType::new();
227                for field in fields {
228                    object.insert(field.name().clone(), Self::try_from(field.data_type())?);
229                }
230                JsonNativeType::Object(object)
231            }
232            t => {
233                return UnsupportedArrowTypeSnafu {
234                    arrow_type: t.clone(),
235                }
236                .fail();
237            }
238        };
239        Ok(t)
240    }
241}
242
243impl Display for JsonNativeType {
244    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
245        match self {
246            JsonNativeType::Null => write!(f, r#""<Null>""#),
247            JsonNativeType::Bool => write!(f, r#""<Bool>""#),
248            JsonNativeType::Number(_) => {
249                write!(f, r#""<Number>""#)
250            }
251            JsonNativeType::String => write!(f, r#""<String>""#),
252            JsonNativeType::Array(item_type) => write!(f, "[{}]", item_type),
253            JsonNativeType::Object(object) => {
254                write!(
255                    f,
256                    "{{{}}}",
257                    object
258                        .iter()
259                        .map(|(k, v)| format!(r#""{k}":{v}"#))
260                        .collect::<Vec<_>>()
261                        .join(",")
262                )
263            }
264            JsonNativeType::Variant => write!(f, r#""<Variant>""#),
265        }
266    }
267}
268
269#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize, Default)]
270pub enum JsonFormat {
271    #[default]
272    Jsonb,
273    Json2(Arc<JsonNativeType>),
274}
275
276/// JsonType is a data type for JSON data. It is stored as binary data of jsonb format.
277/// It utilizes current binary value and vector implementation.
278#[derive(Debug, Default, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize)]
279pub struct JsonType {
280    pub format: JsonFormat,
281}
282
283impl JsonType {
284    pub fn new(format: JsonFormat) -> Self {
285        Self { format }
286    }
287
288    pub(crate) fn json2(json_type: Arc<JsonNativeType>) -> Self {
289        Self {
290            format: JsonFormat::Json2(json_type),
291        }
292    }
293
294    pub fn is_json2(&self) -> bool {
295        matches!(self.format, JsonFormat::Json2(_))
296    }
297
298    /// Returns the native JSON type represented by this data type.
299    pub fn native_type(&self) -> &JsonNativeType {
300        match &self.format {
301            JsonFormat::Jsonb => &JsonNativeType::String,
302            JsonFormat::Json2(x) => x.as_ref(),
303        }
304    }
305
306    pub fn null() -> Self {
307        Self::json2(Arc::new(JsonNativeType::Null))
308    }
309
310    /// Check if it includes all fields in `other` json type.
311    pub fn is_include(&self, other: &JsonType) -> bool {
312        match (&self.format, &other.format) {
313            (JsonFormat::Jsonb, JsonFormat::Jsonb) => true,
314            (JsonFormat::Json2(this), JsonFormat::Json2(that)) => is_include(this, that),
315            _ => false,
316        }
317    }
318}
319
320pub(crate) fn is_include(this: &JsonNativeType, that: &JsonNativeType) -> bool {
321    fn is_include_object(this: &JsonObjectType, that: &JsonObjectType) -> bool {
322        for (type_name, that_type) in that {
323            let Some(this_type) = this.get(type_name) else {
324                return false;
325            };
326            if !is_include(this_type, that_type) {
327                return false;
328            }
329        }
330        true
331    }
332
333    match (this, that) {
334        (this, that) if this == that => true,
335        (JsonNativeType::Array(this), JsonNativeType::Array(that)) => is_include(this, that),
336        (JsonNativeType::Object(this), JsonNativeType::Object(that)) => {
337            is_include_object(this, that)
338        }
339        (_, JsonNativeType::Null) => true,
340        _ => false,
341    }
342}
343
344impl DataType for JsonType {
345    fn name(&self) -> String {
346        match &self.format {
347            JsonFormat::Jsonb => JSON_TYPE_NAME.to_string(),
348            JsonFormat::Json2(ty) => {
349                format!("{JSON2_TYPE_NAME}{}", ty)
350            }
351        }
352    }
353
354    fn logical_type_id(&self) -> LogicalTypeId {
355        LogicalTypeId::Json
356    }
357
358    fn default_value(&self) -> Value {
359        Bytes::default().into()
360    }
361
362    fn as_arrow_type(&self) -> ArrowDataType {
363        match &self.format {
364            JsonFormat::Jsonb => ArrowDataType::Binary,
365            JsonFormat::Json2(x) => {
366                let mut object = JsonNativeType::object();
367                object.merge(x.as_ref());
368                object.as_arrow_type()
369            }
370        }
371    }
372
373    fn create_mutable_vector(&self, capacity: usize) -> Box<dyn MutableVector> {
374        match &self.format {
375            JsonFormat::Jsonb => Box::new(BinaryVectorBuilder::with_capacity(capacity)),
376            // TODO(LFC): Carry JsonSettings in JsonFormat::Json2 and use with_settings here.
377            JsonFormat::Json2(x) => Box::new(JsonVectorBuilder::new(x.as_ref().clone(), capacity)),
378        }
379    }
380
381    fn try_cast(&self, from: Value) -> Option<Value> {
382        match from {
383            Value::Binary(v) => Some(Value::Binary(v)),
384            _ => None,
385        }
386    }
387}
388
389impl Display for JsonType {
390    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
391        write!(f, "{}", self.name())
392    }
393}
394
395/// Converts a json type value to string
396pub fn jsonb_to_string(val: &[u8]) -> Result<String> {
397    if val.is_empty() {
398        return Ok("".to_string());
399    }
400    match jsonb::from_slice(val) {
401        Ok(jsonb_value) => {
402            let serialized = jsonb_value.to_string();
403            fix_unicode_point(&serialized)
404        }
405        Err(e) => InvalidJsonbSnafu { error: e }.fail(),
406    }
407}
408
409/// Converts a json type value to serde_json::Value
410pub fn jsonb_to_serde_json(val: &[u8]) -> Result<serde_json::Value> {
411    let json_string = jsonb_to_string(val)?;
412    serde_json::Value::from_str(&json_string).context(DeserializeSnafu { json: json_string })
413}
414
415/// Normalizes a JSON string by converting Rust-style Unicode escape sequences to JSON-compatible format.
416///
417/// The input is scanned for Rust-style Unicode code
418/// point escapes of the form `\\u{H...}` (a backslash, `u`, an opening brace,
419/// followed by 1–6 hexadecimal digits, and a closing brace). Each such escape is
420/// converted into JSON-compatible UTF‑16 escape sequences:
421///
422/// - For code points in the Basic Multilingual Plane (≤ `0xFFFF`), the escape is
423///   converted to a single JSON `\\uXXXX` sequence with four uppercase hex digits.
424/// - For code points above `0xFFFF` and less than Unicode max code point `0x10FFFF`,
425///   the code point is encoded as a UTF‑16 surrogate pair and emitted as two consecutive
426///   `\\uXXXX` sequences (as JSON format required).
427///
428/// After this normalization, the function returns the normalized string
429fn fix_unicode_point(json: &str) -> Result<String> {
430    static UNICODE_CODE_POINT_PATTERN: LazyLock<Regex> = LazyLock::new(|| {
431        // Match literal "\u{...}" sequences, capturing 1–6 (code point range) hex digits
432        // inside braces.
433        Regex::new(r"\\u\{([0-9a-fA-F]{1,6})}").unwrap_or_else(|e| panic!("{}", e))
434    });
435
436    let v = UNICODE_CODE_POINT_PATTERN.replace_all(json, |caps: &Captures| {
437        // Extract the hex payload (without braces) and parse to a code point.
438        let hex = &caps[1];
439        let Ok(code) = u32::from_str_radix(hex, 16) else {
440            // On parse failure, leave the original escape sequence unchanged.
441            return caps[0].to_string();
442        };
443
444        if code <= 0xFFFF {
445            // Basic Multilingual Plane: JSON can represent this directly as \uXXXX.
446            format!("\\u{:04X}", code)
447        } else if code > 0x10FFFF {
448            // Beyond max Unicode code point
449            caps[0].to_string()
450        } else {
451            // Supplementary planes: JSON needs UTF-16 surrogate pairs.
452            // Convert the code point to a 20-bit value.
453            let code = code - 0x10000;
454
455            // High surrogate: top 10 bits, offset by 0xD800.
456            let high = 0xD800 + ((code >> 10) & 0x3FF);
457
458            // Low surrogate: bottom 10 bits, offset by 0xDC00.
459            let low = 0xDC00 + (code & 0x3FF);
460
461            // Emit two \uXXXX escapes in sequence.
462            format!("\\u{:04X}\\u{:04X}", high, low)
463        }
464    });
465    Ok(v.to_string())
466}
467
468/// Parses a string to a json type value
469pub fn parse_string_to_jsonb(s: &str) -> Result<Vec<u8>> {
470    jsonb::parse_value(s.as_bytes())
471        .map_err(|_| InvalidJsonSnafu { value: s }.build())
472        .map(|json| json.to_vec())
473}
474
475#[cfg(test)]
476mod tests {
477    use super::*;
478
479    #[test]
480    fn test_fix_unicode_point() -> Result<()> {
481        let valid_cases = vec![
482            (r#"{"data": "simple ascii"}"#, r#"{"data": "simple ascii"}"#),
483            (
484                r#"{"data":"Greek sigma: \u{03a3}"}"#,
485                r#"{"data":"Greek sigma: \u03A3"}"#,
486            ),
487            (
488                r#"{"data":"Joker card: \u{1f0df}"}"#,
489                r#"{"data":"Joker card: \uD83C\uDCDF"}"#,
490            ),
491            (
492                r#"{"data":"BMP boundary: \u{ffff}"}"#,
493                r#"{"data":"BMP boundary: \uFFFF"}"#,
494            ),
495            (
496                r#"{"data":"Supplementary min: \u{10000}"}"#,
497                r#"{"data":"Supplementary min: \uD800\uDC00"}"#,
498            ),
499            (
500                r#"{"data":"Supplementary max: \u{10ffff}"}"#,
501                r#"{"data":"Supplementary max: \uDBFF\uDFFF"}"#,
502            ),
503        ];
504        for (input, expect) in valid_cases {
505            let v = fix_unicode_point(input)?;
506            assert_eq!(v, expect);
507        }
508
509        let invalid_escape_cases = vec![
510            (
511                r#"{"data": "Invalid hex: \u{gggg}"}"#,
512                r#"{"data": "Invalid hex: \u{gggg}"}"#,
513            ),
514            (
515                r#"{"data": "Empty braces: \u{}"}"#,
516                r#"{"data": "Empty braces: \u{}"}"#,
517            ),
518            (
519                r#"{"data": "Out of range: \u{1100000}"}"#,
520                r#"{"data": "Out of range: \u{1100000}"}"#,
521            ),
522        ];
523        for (input, expect) in invalid_escape_cases {
524            let v = fix_unicode_point(input)?;
525            assert_eq!(v, expect);
526        }
527
528        Ok(())
529    }
530
531    #[test]
532    fn test_json_type_include() {
533        fn test(this: &JsonNativeType, that: &JsonNativeType, expected: bool) {
534            assert_eq!(is_include(this, that), expected, "this={this}, that={that}");
535        }
536
537        test(&JsonNativeType::Null, &JsonNativeType::Null, true);
538        test(&JsonNativeType::Null, &JsonNativeType::Bool, false);
539        test(&JsonNativeType::Bool, &JsonNativeType::Null, true);
540
541        test(&JsonNativeType::Bool, &JsonNativeType::Bool, true);
542        test(&JsonNativeType::Bool, &JsonNativeType::u64(), false);
543
544        test(&JsonNativeType::u64(), &JsonNativeType::Null, true);
545        test(&JsonNativeType::u64(), &JsonNativeType::u64(), true);
546        test(&JsonNativeType::u64(), &JsonNativeType::String, false);
547
548        test(&JsonNativeType::String, &JsonNativeType::Null, true);
549        test(&JsonNativeType::String, &JsonNativeType::String, true);
550        test(
551            &JsonNativeType::String,
552            &JsonNativeType::Array(Box::new(JsonNativeType::f64())),
553            false,
554        );
555
556        test(
557            &JsonNativeType::Array(Box::new(JsonNativeType::f64())),
558            &JsonNativeType::Null,
559            true,
560        );
561        test(
562            &JsonNativeType::Array(Box::new(JsonNativeType::f64())),
563            &JsonNativeType::Array(Box::new(JsonNativeType::Null)),
564            true,
565        );
566        test(
567            &JsonNativeType::Array(Box::new(JsonNativeType::f64())),
568            &JsonNativeType::Array(Box::new(JsonNativeType::f64())),
569            true,
570        );
571        test(
572            &JsonNativeType::Array(Box::new(JsonNativeType::f64())),
573            &JsonNativeType::String,
574            false,
575        );
576        test(
577            &JsonNativeType::Array(Box::new(JsonNativeType::f64())),
578            &JsonNativeType::Object(JsonObjectType::new()),
579            false,
580        );
581
582        let simple_json_object = &JsonNativeType::Object(JsonObjectType::from([(
583            "foo".to_string(),
584            JsonNativeType::String,
585        )]));
586        test(simple_json_object, simple_json_object, true);
587        test(simple_json_object, &JsonNativeType::i64(), false);
588        test(
589            simple_json_object,
590            &JsonNativeType::Object(JsonObjectType::from([(
591                "bar".to_string(),
592                JsonNativeType::i64(),
593            )])),
594            false,
595        );
596
597        let complex_json_object = &JsonNativeType::Object(JsonObjectType::from([
598            (
599                "nested".to_string(),
600                JsonNativeType::Object(JsonObjectType::from([(
601                    "a".to_string(),
602                    JsonNativeType::Object(JsonObjectType::from([(
603                        "b".to_string(),
604                        JsonNativeType::Object(JsonObjectType::from([(
605                            "c".to_string(),
606                            JsonNativeType::String,
607                        )])),
608                    )])),
609                )])),
610            ),
611            ("bar".to_string(), JsonNativeType::i64()),
612        ]));
613        test(simple_json_object, &JsonNativeType::Null, true);
614        test(complex_json_object, &JsonNativeType::String, false);
615        test(complex_json_object, complex_json_object, true);
616        test(
617            complex_json_object,
618            &JsonNativeType::Object(JsonObjectType::from([(
619                "bar".to_string(),
620                JsonNativeType::i64(),
621            )])),
622            true,
623        );
624        test(
625            complex_json_object,
626            &JsonNativeType::Object(JsonObjectType::from([
627                (
628                    "nested".to_string(),
629                    JsonNativeType::Object(JsonObjectType::from([(
630                        "a".to_string(),
631                        JsonNativeType::Null,
632                    )])),
633                ),
634                ("bar".to_string(), JsonNativeType::i64()),
635            ])),
636            true,
637        );
638        test(
639            complex_json_object,
640            &JsonNativeType::Object(JsonObjectType::from([
641                (
642                    "nested".to_string(),
643                    JsonNativeType::Object(JsonObjectType::from([(
644                        "a".to_string(),
645                        JsonNativeType::String,
646                    )])),
647                ),
648                ("bar".to_string(), JsonNativeType::i64()),
649            ])),
650            false,
651        );
652        test(
653            complex_json_object,
654            &JsonNativeType::Object(JsonObjectType::from([
655                (
656                    "nested".to_string(),
657                    JsonNativeType::Object(JsonObjectType::from([(
658                        "a".to_string(),
659                        JsonNativeType::Object(JsonObjectType::from([(
660                            "b".to_string(),
661                            JsonNativeType::String,
662                        )])),
663                    )])),
664                ),
665                ("bar".to_string(), JsonNativeType::i64()),
666            ])),
667            false,
668        );
669        test(
670            complex_json_object,
671            &JsonNativeType::Object(JsonObjectType::from([
672                (
673                    "nested".to_string(),
674                    JsonNativeType::Object(JsonObjectType::from([(
675                        "a".to_string(),
676                        JsonNativeType::Object(JsonObjectType::from([(
677                            "b".to_string(),
678                            JsonNativeType::Object(JsonObjectType::from([(
679                                "c".to_string(),
680                                JsonNativeType::Null,
681                            )])),
682                        )])),
683                    )])),
684                ),
685                ("bar".to_string(), JsonNativeType::i64()),
686            ])),
687            true,
688        );
689        test(
690            complex_json_object,
691            &JsonNativeType::Object(JsonObjectType::from([
692                (
693                    "nested".to_string(),
694                    JsonNativeType::Object(JsonObjectType::from([(
695                        "a".to_string(),
696                        JsonNativeType::Object(JsonObjectType::from([(
697                            "b".to_string(),
698                            JsonNativeType::Object(JsonObjectType::from([(
699                                "c".to_string(),
700                                JsonNativeType::Bool,
701                            )])),
702                        )])),
703                    )])),
704                ),
705                ("bar".to_string(), JsonNativeType::i64()),
706            ])),
707            false,
708        );
709        test(
710            complex_json_object,
711            &JsonNativeType::Object(JsonObjectType::from([(
712                "nested".to_string(),
713                JsonNativeType::Object(JsonObjectType::from([(
714                    "a".to_string(),
715                    JsonNativeType::Object(JsonObjectType::from([(
716                        "b".to_string(),
717                        JsonNativeType::Object(JsonObjectType::from([(
718                            "c".to_string(),
719                            JsonNativeType::String,
720                        )])),
721                    )])),
722                )])),
723            )])),
724            true,
725        );
726    }
727
728    #[test]
729    fn test_merge_json_type() {
730        fn test(other: JsonNativeType, json_type: &mut JsonNativeType, expected: &str) {
731            json_type.merge(&other);
732            assert_eq!(json_type.to_string(), expected);
733        }
734
735        // Null should be absorbed by a concrete scalar type.
736        test(
737            JsonNativeType::Bool,
738            &mut JsonNativeType::Null,
739            r#""<Bool>""#,
740        );
741
742        // Merging a null value into an existing concrete type should keep the type unchanged.
743        test(
744            JsonNativeType::Null,
745            &mut JsonNativeType::Bool,
746            r#""<Bool>""#,
747        );
748
749        // Identical number categories should stay as Number.
750        test(
751            JsonNativeType::i64(),
752            &mut JsonNativeType::i64(),
753            r#""<Number>""#,
754        );
755
756        // Conflicting number categories should be lifted to Variant.
757        for (mut this, other) in [
758            (JsonNativeType::u64(), JsonNativeType::i64()),
759            (JsonNativeType::u64(), JsonNativeType::f64()),
760            (JsonNativeType::i64(), JsonNativeType::f64()),
761        ] {
762            test(other, &mut this, r#""<Variant>""#);
763        }
764
765        // Object merge should preserve existing fields and append missing fields.
766        test(
767            JsonNativeType::Object(JsonObjectType::from([(
768                "foo".to_string(),
769                JsonNativeType::String,
770            )])),
771            &mut JsonNativeType::Object(JsonObjectType::from([(
772                "bar".to_string(),
773                JsonNativeType::i64(),
774            )])),
775            r#"{"bar":"<Number>","foo":"<String>"}"#,
776        );
777
778        // Conflicting object field types should only lift that field to Variant.
779        test(
780            JsonNativeType::Object(JsonObjectType::from([(
781                "foo".to_string(),
782                JsonNativeType::i64(),
783            )])),
784            &mut JsonNativeType::Object(JsonObjectType::from([(
785                "foo".to_string(),
786                JsonNativeType::Bool,
787            )])),
788            r#"{"foo":"<Variant>"}"#,
789        );
790
791        // Nested objects should merge recursively.
792        test(
793            JsonNativeType::Object(JsonObjectType::from([(
794                "nested".to_string(),
795                JsonNativeType::Object(JsonObjectType::from([(
796                    "foo".to_string(),
797                    JsonNativeType::String,
798                )])),
799            )])),
800            &mut JsonNativeType::Object(JsonObjectType::from([(
801                "nested".to_string(),
802                JsonNativeType::Object(JsonObjectType::from([(
803                    "bar".to_string(),
804                    JsonNativeType::Bool,
805                )])),
806            )])),
807            r#"{"nested":{"bar":"<Bool>","foo":"<String>"}}"#,
808        );
809
810        // Arrays should merge their element types recursively.
811        test(
812            JsonNativeType::Array(Box::new(JsonNativeType::String)),
813            &mut JsonNativeType::Array(Box::new(JsonNativeType::u64())),
814            r#"["<Variant>"]"#,
815        );
816
817        // Root-level incompatible types should be lifted to Variant.
818        test(
819            JsonNativeType::Object(JsonObjectType::from([(
820                "foo".to_string(),
821                JsonNativeType::String,
822            )])),
823            &mut JsonNativeType::Bool,
824            r#""<Variant>""#,
825        );
826    }
827}