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