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::sync::Arc;
18
19use arrow::datatypes::DataType as ArrowDataType;
20use arrow_schema::{Field, Fields};
21use common_base::bytes::Bytes;
22use serde::{Deserialize, Serialize};
23
24use crate::Error;
25use crate::data_type::DataType;
26use crate::error::{InvalidJsonSnafu, InvalidJsonbSnafu, Result, UnsupportedArrowTypeSnafu};
27use crate::prelude::ConcreteDataType;
28use crate::scalars::ScalarVectorBuilder;
29use crate::type_id::LogicalTypeId;
30use crate::value::Value;
31use crate::vectors::json::builder::JsonVectorBuilder;
32use crate::vectors::{BinaryVectorBuilder, MutableVector};
33
34pub const JSON_TYPE_NAME: &str = "Json";
35const JSON2_TYPE_NAME: &str = "Json2";
36
37pub type JsonObjectType = BTreeMap<String, JsonNativeType>;
38
39#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize)]
40pub enum JsonNumberType {
41    U64,
42    I64,
43    F64,
44}
45
46#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize, Default)]
47pub enum JsonNativeType {
48    /// JSON null value type.
49    ///
50    /// This variant may also appear as the initial state while merging inferred
51    /// types, but it does not represent an empty object. Empty objects are
52    /// represented as `Object({})`.
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    pub 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    /// Returns whether this type is a boolean, number, or string scalar.
148    pub fn is_primitive(&self) -> bool {
149        matches!(
150            self,
151            JsonNativeType::Bool | JsonNativeType::Number(_) | JsonNativeType::String
152        )
153    }
154}
155
156impl From<&ConcreteDataType> for JsonNativeType {
157    fn from(value: &ConcreteDataType) -> Self {
158        match value {
159            ConcreteDataType::Null(_) => JsonNativeType::Null,
160            ConcreteDataType::Boolean(_) => JsonNativeType::Bool,
161            ConcreteDataType::UInt64(_)
162            | ConcreteDataType::UInt32(_)
163            | ConcreteDataType::UInt16(_)
164            | ConcreteDataType::UInt8(_) => JsonNativeType::u64(),
165            ConcreteDataType::Int64(_)
166            | ConcreteDataType::Int32(_)
167            | ConcreteDataType::Int16(_)
168            | ConcreteDataType::Int8(_) => JsonNativeType::i64(),
169            ConcreteDataType::Float64(_) | ConcreteDataType::Float32(_) => JsonNativeType::f64(),
170            ConcreteDataType::String(_) => JsonNativeType::String,
171            ConcreteDataType::List(list_type) => {
172                JsonNativeType::Array(Box::new(JsonNativeType::from(list_type.item_type())))
173            }
174            ConcreteDataType::Struct(struct_type) => JsonNativeType::Object(
175                struct_type
176                    .fields()
177                    .iter()
178                    .map(|field| (field.name().to_string(), field.data_type().into()))
179                    .collect(),
180            ),
181            ConcreteDataType::Json(json_type) => json_type.native_type().clone(),
182            ConcreteDataType::Binary(_) => JsonNativeType::Variant,
183            _ => unreachable!(),
184        }
185    }
186}
187
188impl TryFrom<&ArrowDataType> for JsonNativeType {
189    type Error = Error;
190
191    fn try_from(t: &ArrowDataType) -> Result<Self> {
192        let t = match t {
193            ArrowDataType::Null => JsonNativeType::Null,
194            ArrowDataType::Boolean => JsonNativeType::Bool,
195            ArrowDataType::Int8
196            | ArrowDataType::Int16
197            | ArrowDataType::Int32
198            | ArrowDataType::Int64 => JsonNativeType::i64(),
199            ArrowDataType::UInt8
200            | ArrowDataType::UInt16
201            | ArrowDataType::UInt32
202            | ArrowDataType::UInt64 => JsonNativeType::u64(),
203            ArrowDataType::Float16 | ArrowDataType::Float32 | ArrowDataType::Float64 => {
204                JsonNativeType::f64()
205            }
206            ArrowDataType::Binary
207            | ArrowDataType::FixedSizeBinary(_)
208            | ArrowDataType::LargeBinary
209            | ArrowDataType::BinaryView => JsonNativeType::Variant,
210            ArrowDataType::Utf8 | ArrowDataType::LargeUtf8 | ArrowDataType::Utf8View => {
211                JsonNativeType::String
212            }
213            ArrowDataType::List(field)
214            | ArrowDataType::ListView(field)
215            | ArrowDataType::FixedSizeList(field, _)
216            | ArrowDataType::LargeList(field)
217            | ArrowDataType::LargeListView(field) => {
218                JsonNativeType::Array(Box::new(Self::try_from(field.data_type())?))
219            }
220            ArrowDataType::Struct(fields) => {
221                let mut object = JsonObjectType::new();
222                for field in fields {
223                    object.insert(field.name().clone(), Self::try_from(field.data_type())?);
224                }
225                JsonNativeType::Object(object)
226            }
227            t => {
228                return UnsupportedArrowTypeSnafu {
229                    arrow_type: t.clone(),
230                }
231                .fail();
232            }
233        };
234        Ok(t)
235    }
236}
237
238impl Display for JsonNativeType {
239    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
240        match self {
241            JsonNativeType::Null => write!(f, r#""<Null>""#),
242            JsonNativeType::Bool => write!(f, r#""<Bool>""#),
243            JsonNativeType::Number(_) => {
244                write!(f, r#""<Number>""#)
245            }
246            JsonNativeType::String => write!(f, r#""<String>""#),
247            JsonNativeType::Array(item_type) => write!(f, "[{}]", item_type),
248            JsonNativeType::Object(object) => {
249                write!(
250                    f,
251                    "{{{}}}",
252                    object
253                        .iter()
254                        .map(|(k, v)| format!(r#""{k}":{v}"#))
255                        .collect::<Vec<_>>()
256                        .join(",")
257                )
258            }
259            JsonNativeType::Variant => write!(f, r#""<Variant>""#),
260        }
261    }
262}
263
264#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize, Default)]
265pub enum JsonFormat {
266    #[default]
267    Jsonb,
268    Json2(Arc<JsonNativeType>),
269}
270
271/// JsonType is a data type for JSON data. It is stored as binary data of jsonb format.
272/// It utilizes current binary value and vector implementation.
273#[derive(Debug, Default, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize)]
274pub struct JsonType {
275    pub format: JsonFormat,
276}
277
278impl JsonType {
279    pub fn new(format: JsonFormat) -> Self {
280        Self { format }
281    }
282
283    pub(crate) fn json2(json_type: Arc<JsonNativeType>) -> Self {
284        Self {
285            format: JsonFormat::Json2(json_type),
286        }
287    }
288
289    pub fn is_json2(&self) -> bool {
290        matches!(self.format, JsonFormat::Json2(_))
291    }
292
293    /// Returns the native JSON type represented by this data type.
294    pub fn native_type(&self) -> &JsonNativeType {
295        match &self.format {
296            JsonFormat::Jsonb => &JsonNativeType::String,
297            JsonFormat::Json2(x) => x.as_ref(),
298        }
299    }
300
301    pub fn null() -> Self {
302        Self::json2(Arc::new(JsonNativeType::Null))
303    }
304
305    /// Check if it includes all fields in `other` json type.
306    pub fn is_include(&self, other: &JsonType) -> bool {
307        match (&self.format, &other.format) {
308            (JsonFormat::Jsonb, JsonFormat::Jsonb) => true,
309            (JsonFormat::Json2(this), JsonFormat::Json2(that)) => is_include(this, that),
310            _ => false,
311        }
312    }
313}
314
315pub(crate) fn is_include(this: &JsonNativeType, that: &JsonNativeType) -> bool {
316    fn is_include_object(this: &JsonObjectType, that: &JsonObjectType) -> bool {
317        for (type_name, that_type) in that {
318            let Some(this_type) = this.get(type_name) else {
319                return false;
320            };
321            if !is_include(this_type, that_type) {
322                return false;
323            }
324        }
325        true
326    }
327
328    match (this, that) {
329        (this, that) if this == that => true,
330        (JsonNativeType::Array(this), JsonNativeType::Array(that)) => is_include(this, that),
331        (JsonNativeType::Object(this), JsonNativeType::Object(that)) => {
332            is_include_object(this, that)
333        }
334        (_, JsonNativeType::Null) => true,
335        _ => false,
336    }
337}
338
339impl DataType for JsonType {
340    fn name(&self) -> String {
341        match &self.format {
342            JsonFormat::Jsonb => JSON_TYPE_NAME.to_string(),
343            JsonFormat::Json2(ty) => {
344                format!("{JSON2_TYPE_NAME}{}", ty)
345            }
346        }
347    }
348
349    fn logical_type_id(&self) -> LogicalTypeId {
350        LogicalTypeId::Json
351    }
352
353    fn default_value(&self) -> Value {
354        Bytes::default().into()
355    }
356
357    fn as_arrow_type(&self) -> ArrowDataType {
358        match &self.format {
359            JsonFormat::Jsonb => ArrowDataType::Binary,
360            JsonFormat::Json2(x) => {
361                let mut object = JsonNativeType::object();
362                object.merge(x.as_ref());
363                object.as_arrow_type()
364            }
365        }
366    }
367
368    fn create_mutable_vector(&self, capacity: usize) -> Box<dyn MutableVector> {
369        match &self.format {
370            JsonFormat::Jsonb => Box::new(BinaryVectorBuilder::with_capacity(capacity)),
371            // TODO(LFC): Carry JsonSettings in JsonFormat::Json2 and use with_settings here.
372            JsonFormat::Json2(x) => Box::new(JsonVectorBuilder::new(x.as_ref().clone(), capacity)),
373        }
374    }
375
376    fn try_cast(&self, from: Value) -> Option<Value> {
377        match from {
378            Value::Binary(v) => Some(Value::Binary(v)),
379            _ => None,
380        }
381    }
382}
383
384impl Display for JsonType {
385    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
386        write!(f, "{}", self.name())
387    }
388}
389
390/// Converts a json type value to string
391///
392/// The binary jsonb is serialized directly with `serde_json`, so both object keys and
393/// string values are escaped by `serde_json`. `RawJsonb::to_string()` is deliberately
394/// avoided, since it falls back to the lossy raw bytes when serialization fails.
395///
396/// Some write paths (e.g. `COPY FROM` csv/json/parquet files, or a JSON parameter bound
397/// as text through the MySQL protocol) currently store the JSON *text* as the column
398/// bytes instead of binary jsonb, and such data may already be persisted. When the bytes
399/// are not valid jsonb but are valid JSON text, they are re-encoded as jsonb and then
400/// serialized the same way, so the output is still escaped by `serde_json`. Anything else
401/// is reported as an error.
402pub fn jsonb_to_string(val: &[u8]) -> Result<String> {
403    if val.is_empty() {
404        return Ok("".to_string());
405    }
406    match serde_json::to_string(&jsonb::RawJsonb::new(val)) {
407        Ok(s) => Ok(s),
408        Err(e) => match jsonb::parse_value_standard_mode(val) {
409            Ok(json) => serde_json::to_string(&jsonb::RawJsonb::new(&json.to_vec())).map_err(|e| {
410                InvalidJsonSnafu {
411                    value: e.to_string(),
412                }
413                .build()
414            }),
415            Err(_) => InvalidJsonSnafu {
416                value: e.to_string(),
417            }
418            .fail(),
419        },
420    }
421}
422
423/// Converts a json type value to serde_json::Value
424pub fn jsonb_to_serde_json(val: &[u8]) -> Result<serde_json::Value> {
425    jsonb::from_slice(val)
426        .map(Into::into)
427        .map_err(|error| InvalidJsonbSnafu { error }.build())
428}
429
430/// Parses a string to a json type value
431pub fn parse_string_to_jsonb(s: &str) -> Result<Vec<u8>> {
432    jsonb::parse_value_standard_mode(s.as_bytes())
433        .map_err(|_| InvalidJsonSnafu { value: s }.build())
434        .map(|json| json.to_vec())
435}
436
437#[cfg(test)]
438mod tests {
439    #[test]
440    fn test_parse_json_rejects_extended_syntax() {
441        for input in ["+1", "01", ".5", "[1,,2]", "{'a':1}"] {
442            assert!(parse_string_to_jsonb(input).is_err(), "{input}");
443        }
444    }
445
446    #[test]
447    fn test_v2_jsonb_to_serde_json_escaped_keys()
448    -> std::result::Result<(), Box<dyn std::error::Error>> {
449        let expected = serde_json::json!({"": 9, "a\"b": {"a\\b": "\u{1f600}\n"}});
450        let input = expected.to_string();
451        let value = jsonb::parse_value(input.as_bytes()).map_err(|e| e.to_string())?;
452        assert_eq!(jsonb_to_serde_json(&value.to_vec())?, expected);
453        Ok(())
454    }
455
456    #[test]
457    fn test_jsonb_to_string_escapes_object_keys()
458    -> std::result::Result<(), Box<dyn std::error::Error>> {
459        let expected = serde_json::json!({
460            "": 1,
461            "a\"b": {"c\\d": ["x", {"e\nf\tg\u{1}": true}], "h": null},
462            "plain": "v\"q",
463        });
464        let input = expected.to_string();
465        let value = jsonb::parse_value(input.as_bytes()).map_err(|e| e.to_string())?;
466        let text = jsonb_to_string(&value.to_vec())?;
467        assert_eq!(serde_json::from_str::<serde_json::Value>(&text)?, expected);
468        assert!(text.contains(r#""a\"b":"#), "{text}");
469        Ok(())
470    }
471
472    #[test]
473    fn test_jsonb_to_string_preserves_literal_unicode_escape_key() {
474        let input = r#"{"\\u{1f600}":"value"}"#;
475        let bytes = parse_string_to_jsonb(input).unwrap();
476        let text = jsonb_to_string(&bytes).unwrap();
477        assert_eq!(
478            serde_json::from_str::<serde_json::Value>(&text).unwrap(),
479            serde_json::from_str::<serde_json::Value>(input).unwrap(),
480        );
481
482        // A BMP literal key round-trips unchanged as well.
483        let input = r#"{"\\u{41}":"value"}"#;
484        let bytes = parse_string_to_jsonb(input).unwrap();
485        let text = jsonb_to_string(&bytes).unwrap();
486        assert_eq!(text, input);
487    }
488
489    #[test]
490    fn test_jsonb_to_string_escapes_string_values() {
491        // Literal `\u{...}` text and a NUL in a value must still render as valid JSON
492        // that round-trips to the same value.
493        for input in [
494            r#"{"k":"\\u{1f600}"}"#,
495            r#"{"k":"\\u{41}"}"#,
496            r#"{"k":"a\u0000b"}"#,
497            r#"["\\u{1f600}","\u0000"]"#,
498        ] {
499            let bytes = parse_string_to_jsonb(input).unwrap();
500            let text = jsonb_to_string(&bytes).unwrap();
501            assert_eq!(
502                serde_json::from_str::<serde_json::Value>(&text).unwrap(),
503                serde_json::from_str::<serde_json::Value>(input).unwrap(),
504                "{input} -> {text}"
505            );
506        }
507
508        // U+2028 is emitted as-is, which is still valid JSON.
509        let bytes = parse_string_to_jsonb("{\"k\":\"a\\u2028b\"}").unwrap();
510        assert_eq!(jsonb_to_string(&bytes).unwrap(), "{\"k\":\"a\u{2028}b\"}");
511    }
512
513    #[test]
514    fn test_jsonb_to_string_rejects_invalid_jsonb() {
515        assert!(jsonb_to_string(&[1, 2, 3, 4]).is_err());
516        assert_eq!(jsonb_to_string(&[]).unwrap(), "");
517    }
518
519    #[test]
520    fn test_jsonb_to_string_accepts_json_text_bytes() {
521        // Bytes written as JSON text rather than binary jsonb (e.g. by `COPY FROM`).
522        assert_eq!(
523            jsonb_to_string(br#"{"foo":"bar"}"#).unwrap(),
524            r#"{"foo":"bar"}"#
525        );
526        assert_eq!(
527            jsonb_to_string(br#"{"a":null, "foo":"bar"}"#).unwrap(),
528            r#"{"a":null,"foo":"bar"}"#
529        );
530        // Keys and values coming from text are still escaped by serde_json.
531        assert_eq!(
532            jsonb_to_string(br#"{"a\"b":"c\u0000d"}"#).unwrap(),
533            r#"{"a\"b":"c\u0000d"}"#
534        );
535        assert!(jsonb_to_string(b"{not json").is_err());
536    }
537
538    #[test]
539    fn test_jsonb_to_string_keeps_compact_format() -> Result<()> {
540        let value = jsonb::parse_value(br#"{"b": [1, 2.5, "x"], "a": {"c": false}}"#).unwrap();
541        assert_eq!(
542            jsonb_to_string(&value.to_vec())?,
543            r#"{"a":{"c":false},"b":[1,2.5,"x"]}"#
544        );
545        Ok(())
546    }
547
548    use super::*;
549
550    #[test]
551    fn test_json_type_include() {
552        fn test(this: &JsonNativeType, that: &JsonNativeType, expected: bool) {
553            assert_eq!(is_include(this, that), expected, "this={this}, that={that}");
554        }
555
556        test(&JsonNativeType::Null, &JsonNativeType::Null, true);
557        test(&JsonNativeType::Null, &JsonNativeType::Bool, false);
558        test(&JsonNativeType::Bool, &JsonNativeType::Null, true);
559
560        test(&JsonNativeType::Bool, &JsonNativeType::Bool, true);
561        test(&JsonNativeType::Bool, &JsonNativeType::u64(), false);
562
563        test(&JsonNativeType::u64(), &JsonNativeType::Null, true);
564        test(&JsonNativeType::u64(), &JsonNativeType::u64(), true);
565        test(&JsonNativeType::u64(), &JsonNativeType::String, false);
566
567        test(&JsonNativeType::String, &JsonNativeType::Null, true);
568        test(&JsonNativeType::String, &JsonNativeType::String, true);
569        test(
570            &JsonNativeType::String,
571            &JsonNativeType::Array(Box::new(JsonNativeType::f64())),
572            false,
573        );
574
575        test(
576            &JsonNativeType::Array(Box::new(JsonNativeType::f64())),
577            &JsonNativeType::Null,
578            true,
579        );
580        test(
581            &JsonNativeType::Array(Box::new(JsonNativeType::f64())),
582            &JsonNativeType::Array(Box::new(JsonNativeType::Null)),
583            true,
584        );
585        test(
586            &JsonNativeType::Array(Box::new(JsonNativeType::f64())),
587            &JsonNativeType::Array(Box::new(JsonNativeType::f64())),
588            true,
589        );
590        test(
591            &JsonNativeType::Array(Box::new(JsonNativeType::f64())),
592            &JsonNativeType::String,
593            false,
594        );
595        test(
596            &JsonNativeType::Array(Box::new(JsonNativeType::f64())),
597            &JsonNativeType::Object(JsonObjectType::new()),
598            false,
599        );
600
601        let simple_json_object = &JsonNativeType::Object(JsonObjectType::from([(
602            "foo".to_string(),
603            JsonNativeType::String,
604        )]));
605        test(simple_json_object, simple_json_object, true);
606        test(simple_json_object, &JsonNativeType::i64(), false);
607        test(
608            simple_json_object,
609            &JsonNativeType::Object(JsonObjectType::from([(
610                "bar".to_string(),
611                JsonNativeType::i64(),
612            )])),
613            false,
614        );
615
616        let complex_json_object = &JsonNativeType::Object(JsonObjectType::from([
617            (
618                "nested".to_string(),
619                JsonNativeType::Object(JsonObjectType::from([(
620                    "a".to_string(),
621                    JsonNativeType::Object(JsonObjectType::from([(
622                        "b".to_string(),
623                        JsonNativeType::Object(JsonObjectType::from([(
624                            "c".to_string(),
625                            JsonNativeType::String,
626                        )])),
627                    )])),
628                )])),
629            ),
630            ("bar".to_string(), JsonNativeType::i64()),
631        ]));
632        test(simple_json_object, &JsonNativeType::Null, true);
633        test(complex_json_object, &JsonNativeType::String, false);
634        test(complex_json_object, complex_json_object, true);
635        test(
636            complex_json_object,
637            &JsonNativeType::Object(JsonObjectType::from([(
638                "bar".to_string(),
639                JsonNativeType::i64(),
640            )])),
641            true,
642        );
643        test(
644            complex_json_object,
645            &JsonNativeType::Object(JsonObjectType::from([
646                (
647                    "nested".to_string(),
648                    JsonNativeType::Object(JsonObjectType::from([(
649                        "a".to_string(),
650                        JsonNativeType::Null,
651                    )])),
652                ),
653                ("bar".to_string(), JsonNativeType::i64()),
654            ])),
655            true,
656        );
657        test(
658            complex_json_object,
659            &JsonNativeType::Object(JsonObjectType::from([
660                (
661                    "nested".to_string(),
662                    JsonNativeType::Object(JsonObjectType::from([(
663                        "a".to_string(),
664                        JsonNativeType::String,
665                    )])),
666                ),
667                ("bar".to_string(), JsonNativeType::i64()),
668            ])),
669            false,
670        );
671        test(
672            complex_json_object,
673            &JsonNativeType::Object(JsonObjectType::from([
674                (
675                    "nested".to_string(),
676                    JsonNativeType::Object(JsonObjectType::from([(
677                        "a".to_string(),
678                        JsonNativeType::Object(JsonObjectType::from([(
679                            "b".to_string(),
680                            JsonNativeType::String,
681                        )])),
682                    )])),
683                ),
684                ("bar".to_string(), JsonNativeType::i64()),
685            ])),
686            false,
687        );
688        test(
689            complex_json_object,
690            &JsonNativeType::Object(JsonObjectType::from([
691                (
692                    "nested".to_string(),
693                    JsonNativeType::Object(JsonObjectType::from([(
694                        "a".to_string(),
695                        JsonNativeType::Object(JsonObjectType::from([(
696                            "b".to_string(),
697                            JsonNativeType::Object(JsonObjectType::from([(
698                                "c".to_string(),
699                                JsonNativeType::Null,
700                            )])),
701                        )])),
702                    )])),
703                ),
704                ("bar".to_string(), JsonNativeType::i64()),
705            ])),
706            true,
707        );
708        test(
709            complex_json_object,
710            &JsonNativeType::Object(JsonObjectType::from([
711                (
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::Bool,
720                            )])),
721                        )])),
722                    )])),
723                ),
724                ("bar".to_string(), JsonNativeType::i64()),
725            ])),
726            false,
727        );
728        test(
729            complex_json_object,
730            &JsonNativeType::Object(JsonObjectType::from([(
731                "nested".to_string(),
732                JsonNativeType::Object(JsonObjectType::from([(
733                    "a".to_string(),
734                    JsonNativeType::Object(JsonObjectType::from([(
735                        "b".to_string(),
736                        JsonNativeType::Object(JsonObjectType::from([(
737                            "c".to_string(),
738                            JsonNativeType::String,
739                        )])),
740                    )])),
741                )])),
742            )])),
743            true,
744        );
745    }
746
747    #[test]
748    fn test_merge_json_type() {
749        fn test(other: JsonNativeType, json_type: &mut JsonNativeType, expected: &str) {
750            json_type.merge(&other);
751            assert_eq!(json_type.to_string(), expected);
752        }
753
754        // Null should be absorbed by a concrete scalar type.
755        test(
756            JsonNativeType::Bool,
757            &mut JsonNativeType::Null,
758            r#""<Bool>""#,
759        );
760
761        // Merging a null value into an existing concrete type should keep the type unchanged.
762        test(
763            JsonNativeType::Null,
764            &mut JsonNativeType::Bool,
765            r#""<Bool>""#,
766        );
767
768        // Identical number categories should stay as Number.
769        test(
770            JsonNativeType::i64(),
771            &mut JsonNativeType::i64(),
772            r#""<Number>""#,
773        );
774
775        // Conflicting number categories should be lifted to Variant.
776        for (mut this, other) in [
777            (JsonNativeType::u64(), JsonNativeType::i64()),
778            (JsonNativeType::u64(), JsonNativeType::f64()),
779            (JsonNativeType::i64(), JsonNativeType::f64()),
780        ] {
781            test(other, &mut this, r#""<Variant>""#);
782        }
783
784        // Object merge should preserve existing fields and append missing fields.
785        test(
786            JsonNativeType::Object(JsonObjectType::from([(
787                "foo".to_string(),
788                JsonNativeType::String,
789            )])),
790            &mut JsonNativeType::Object(JsonObjectType::from([(
791                "bar".to_string(),
792                JsonNativeType::i64(),
793            )])),
794            r#"{"bar":"<Number>","foo":"<String>"}"#,
795        );
796
797        // Conflicting object field types should only lift that field to Variant.
798        test(
799            JsonNativeType::Object(JsonObjectType::from([(
800                "foo".to_string(),
801                JsonNativeType::i64(),
802            )])),
803            &mut JsonNativeType::Object(JsonObjectType::from([(
804                "foo".to_string(),
805                JsonNativeType::Bool,
806            )])),
807            r#"{"foo":"<Variant>"}"#,
808        );
809
810        // Nested objects should merge recursively.
811        test(
812            JsonNativeType::Object(JsonObjectType::from([(
813                "nested".to_string(),
814                JsonNativeType::Object(JsonObjectType::from([(
815                    "foo".to_string(),
816                    JsonNativeType::String,
817                )])),
818            )])),
819            &mut JsonNativeType::Object(JsonObjectType::from([(
820                "nested".to_string(),
821                JsonNativeType::Object(JsonObjectType::from([(
822                    "bar".to_string(),
823                    JsonNativeType::Bool,
824                )])),
825            )])),
826            r#"{"nested":{"bar":"<Bool>","foo":"<String>"}}"#,
827        );
828
829        // Arrays should merge their element types recursively.
830        test(
831            JsonNativeType::Array(Box::new(JsonNativeType::String)),
832            &mut JsonNativeType::Array(Box::new(JsonNativeType::u64())),
833            r#"["<Variant>"]"#,
834        );
835
836        // Root-level incompatible types should be lifted to Variant.
837        test(
838            JsonNativeType::Object(JsonObjectType::from([(
839                "foo".to_string(),
840                JsonNativeType::String,
841            )])),
842            &mut JsonNativeType::Bool,
843            r#""<Variant>""#,
844        );
845    }
846}