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