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 object keys and string
393/// values are escaped by `serde_json`. `RawJsonb::to_string()` is avoided, since it falls
394/// back to the lossy raw bytes when serialization fails.
395///
396/// Some write paths (e.g. `COPY FROM` files, or a JSON parameter bound as text through
397/// the MySQL protocol) store JSON text in place of binary jsonb. Bytes that fail to
398/// serialize are parsed as text with the extended parser, as `jsonb::from_slice` does.
399pub fn jsonb_to_string(val: &[u8]) -> Result<String> {
400    if val.is_empty() {
401        return Ok("".to_string());
402    }
403    match serde_json::to_string(&jsonb::RawJsonb::new(val)) {
404        Ok(s) => Ok(s),
405        Err(jsonb_err) => {
406            let value = jsonb::parse_value(val).map_err(|text_err| {
407                InvalidJsonSnafu {
408                    value: format!("neither jsonb ({jsonb_err}) nor JSON text ({text_err})"),
409                }
410                .build()
411            })?;
412            serde_json::to_string(&jsonb::RawJsonb::new(&value.to_vec())).map_err(|e| {
413                InvalidJsonSnafu {
414                    value: e.to_string(),
415                }
416                .build()
417            })
418        }
419    }
420}
421
422/// Converts a json type value to serde_json::Value
423pub fn jsonb_to_serde_json(val: &[u8]) -> Result<serde_json::Value> {
424    jsonb::from_slice(val)
425        .map(Into::into)
426        .map_err(|error| InvalidJsonbSnafu { error }.build())
427}
428
429/// Parses a string to a json type value
430pub fn parse_string_to_jsonb(s: &str) -> Result<Vec<u8>> {
431    jsonb::parse_value_standard_mode(s.as_bytes())
432        .map_err(|_| InvalidJsonSnafu { value: s }.build())
433        .map(|json| json.to_vec())
434}
435
436#[cfg(test)]
437mod tests {
438    #[test]
439    fn test_parse_json_rejects_extended_syntax() {
440        for input in ["+1", "01", ".5", "[1,,2]", "{'a':1}"] {
441            assert!(parse_string_to_jsonb(input).is_err(), "{input}");
442        }
443    }
444
445    #[test]
446    fn test_v2_jsonb_to_serde_json_escaped_keys()
447    -> std::result::Result<(), Box<dyn std::error::Error>> {
448        let expected = serde_json::json!({"": 9, "a\"b": {"a\\b": "\u{1f600}\n"}});
449        let input = expected.to_string();
450        let value = jsonb::parse_value(input.as_bytes()).map_err(|e| e.to_string())?;
451        assert_eq!(jsonb_to_serde_json(&value.to_vec())?, expected);
452        Ok(())
453    }
454
455    #[test]
456    fn test_jsonb_to_serde_json_scalars() {
457        for input in [r#""2026-11-05""#, "1730800000000", "false", "null"] {
458            let expected: serde_json::Value = serde_json::from_str(input).unwrap();
459            let jsonb = parse_string_to_jsonb(input).unwrap();
460            assert_eq!(jsonb_to_serde_json(&jsonb).unwrap(), expected, "{input}");
461            // Stored as JSON text, the first two used to decode as `false`.
462            assert_eq!(
463                jsonb_to_serde_json(input.as_bytes()).unwrap(),
464                expected,
465                "{input}"
466            );
467        }
468    }
469
470    #[test]
471    fn test_jsonb_to_string_escapes_object_keys()
472    -> std::result::Result<(), Box<dyn std::error::Error>> {
473        let expected = serde_json::json!({
474            "": 1,
475            "a\"b": {"c\\d": ["x", {"e\nf\tg\u{1}": true}], "h": null},
476            "plain": "v\"q",
477        });
478        let input = expected.to_string();
479        let value = jsonb::parse_value(input.as_bytes()).map_err(|e| e.to_string())?;
480        let text = jsonb_to_string(&value.to_vec())?;
481        assert_eq!(serde_json::from_str::<serde_json::Value>(&text)?, expected);
482        assert!(text.contains(r#""a\"b":"#), "{text}");
483        Ok(())
484    }
485
486    #[test]
487    fn test_jsonb_to_string_preserves_literal_unicode_escape_key() {
488        let input = r#"{"\\u{1f600}":"value"}"#;
489        let bytes = parse_string_to_jsonb(input).unwrap();
490        let text = jsonb_to_string(&bytes).unwrap();
491        assert_eq!(
492            serde_json::from_str::<serde_json::Value>(&text).unwrap(),
493            serde_json::from_str::<serde_json::Value>(input).unwrap(),
494        );
495
496        // A BMP literal key round-trips unchanged as well.
497        let input = r#"{"\\u{41}":"value"}"#;
498        let bytes = parse_string_to_jsonb(input).unwrap();
499        let text = jsonb_to_string(&bytes).unwrap();
500        assert_eq!(text, input);
501    }
502
503    #[test]
504    fn test_jsonb_to_string_escapes_string_values() {
505        // Literal `\u{...}` text and a NUL in a value must still render as valid JSON
506        // that round-trips to the same value.
507        for input in [
508            r#"{"k":"\\u{1f600}"}"#,
509            r#"{"k":"\\u{41}"}"#,
510            r#"{"k":"a\u0000b"}"#,
511            r#"["\\u{1f600}","\u0000"]"#,
512        ] {
513            let bytes = parse_string_to_jsonb(input).unwrap();
514            let text = jsonb_to_string(&bytes).unwrap();
515            assert_eq!(
516                serde_json::from_str::<serde_json::Value>(&text).unwrap(),
517                serde_json::from_str::<serde_json::Value>(input).unwrap(),
518                "{input} -> {text}"
519            );
520        }
521
522        // U+2028 is emitted as-is, which is still valid JSON.
523        let bytes = parse_string_to_jsonb("{\"k\":\"a\\u2028b\"}").unwrap();
524        assert_eq!(jsonb_to_string(&bytes).unwrap(), "{\"k\":\"a\u{2028}b\"}");
525    }
526
527    #[test]
528    fn test_jsonb_to_string_rejects_invalid_jsonb() {
529        assert!(jsonb_to_string(&[1, 2, 3, 4]).is_err());
530        assert_eq!(jsonb_to_string(&[]).unwrap(), "");
531
532        let mut truncated = parse_string_to_jsonb(r#"{"k":"value"}"#).unwrap();
533        truncated.pop();
534        assert!(jsonb_to_string(&truncated).is_err());
535    }
536
537    #[test]
538    fn test_jsonb_to_string_accepts_json_text_bytes() {
539        // Bytes written as JSON text rather than binary jsonb (e.g. by `COPY FROM`).
540        assert_eq!(
541            jsonb_to_string(br#"{"foo":"bar"}"#).unwrap(),
542            r#"{"foo":"bar"}"#
543        );
544        assert_eq!(
545            jsonb_to_string(br#"{"a":null, "foo":"bar"}"#).unwrap(),
546            r#"{"a":null,"foo":"bar"}"#
547        );
548        // Keys and values coming from text are still escaped by serde_json.
549        assert_eq!(
550            jsonb_to_string(br#"{"a\"b":"c\u0000d"}"#).unwrap(),
551            r#"{"a\"b":"c\u0000d"}"#
552        );
553        // These start with bytes that also read as a jsonb scalar header.
554        assert_eq!(
555            jsonb_to_string(br#""hello world""#).unwrap(),
556            r#""hello world""#
557        );
558        assert_eq!(jsonb_to_string(b"        null").unwrap(), "null");
559        // Without the bounds check, these read back as `false`.
560        assert_eq!(
561            jsonb_to_string(br#""2026-11-05""#).unwrap(),
562            r#""2026-11-05""#
563        );
564        assert_eq!(jsonb_to_string(b"1730800000000").unwrap(), "1730800000000");
565        // Extended syntax is accepted, as text was read before.
566        assert_eq!(jsonb_to_string(b"[+1,,2]").unwrap(), "[1,null,2]");
567        assert!(jsonb_to_string(b"{not json").is_err());
568    }
569
570    #[test]
571    fn test_jsonb_to_string_keeps_compact_format() -> Result<()> {
572        let value = jsonb::parse_value(br#"{"b": [1, 2.5, "x"], "a": {"c": false}}"#).unwrap();
573        assert_eq!(
574            jsonb_to_string(&value.to_vec())?,
575            r#"{"a":{"c":false},"b":[1,2.5,"x"]}"#
576        );
577        Ok(())
578    }
579
580    use super::*;
581
582    #[test]
583    fn test_json_type_include() {
584        fn test(this: &JsonNativeType, that: &JsonNativeType, expected: bool) {
585            assert_eq!(is_include(this, that), expected, "this={this}, that={that}");
586        }
587
588        test(&JsonNativeType::Null, &JsonNativeType::Null, true);
589        test(&JsonNativeType::Null, &JsonNativeType::Bool, false);
590        test(&JsonNativeType::Bool, &JsonNativeType::Null, true);
591
592        test(&JsonNativeType::Bool, &JsonNativeType::Bool, true);
593        test(&JsonNativeType::Bool, &JsonNativeType::u64(), false);
594
595        test(&JsonNativeType::u64(), &JsonNativeType::Null, true);
596        test(&JsonNativeType::u64(), &JsonNativeType::u64(), true);
597        test(&JsonNativeType::u64(), &JsonNativeType::String, false);
598
599        test(&JsonNativeType::String, &JsonNativeType::Null, true);
600        test(&JsonNativeType::String, &JsonNativeType::String, true);
601        test(
602            &JsonNativeType::String,
603            &JsonNativeType::Array(Box::new(JsonNativeType::f64())),
604            false,
605        );
606
607        test(
608            &JsonNativeType::Array(Box::new(JsonNativeType::f64())),
609            &JsonNativeType::Null,
610            true,
611        );
612        test(
613            &JsonNativeType::Array(Box::new(JsonNativeType::f64())),
614            &JsonNativeType::Array(Box::new(JsonNativeType::Null)),
615            true,
616        );
617        test(
618            &JsonNativeType::Array(Box::new(JsonNativeType::f64())),
619            &JsonNativeType::Array(Box::new(JsonNativeType::f64())),
620            true,
621        );
622        test(
623            &JsonNativeType::Array(Box::new(JsonNativeType::f64())),
624            &JsonNativeType::String,
625            false,
626        );
627        test(
628            &JsonNativeType::Array(Box::new(JsonNativeType::f64())),
629            &JsonNativeType::Object(JsonObjectType::new()),
630            false,
631        );
632
633        let simple_json_object = &JsonNativeType::Object(JsonObjectType::from([(
634            "foo".to_string(),
635            JsonNativeType::String,
636        )]));
637        test(simple_json_object, simple_json_object, true);
638        test(simple_json_object, &JsonNativeType::i64(), false);
639        test(
640            simple_json_object,
641            &JsonNativeType::Object(JsonObjectType::from([(
642                "bar".to_string(),
643                JsonNativeType::i64(),
644            )])),
645            false,
646        );
647
648        let complex_json_object = &JsonNativeType::Object(JsonObjectType::from([
649            (
650                "nested".to_string(),
651                JsonNativeType::Object(JsonObjectType::from([(
652                    "a".to_string(),
653                    JsonNativeType::Object(JsonObjectType::from([(
654                        "b".to_string(),
655                        JsonNativeType::Object(JsonObjectType::from([(
656                            "c".to_string(),
657                            JsonNativeType::String,
658                        )])),
659                    )])),
660                )])),
661            ),
662            ("bar".to_string(), JsonNativeType::i64()),
663        ]));
664        test(simple_json_object, &JsonNativeType::Null, true);
665        test(complex_json_object, &JsonNativeType::String, false);
666        test(complex_json_object, complex_json_object, true);
667        test(
668            complex_json_object,
669            &JsonNativeType::Object(JsonObjectType::from([(
670                "bar".to_string(),
671                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::Null,
683                    )])),
684                ),
685                ("bar".to_string(), JsonNativeType::i64()),
686            ])),
687            true,
688        );
689        test(
690            complex_json_object,
691            &JsonNativeType::Object(JsonObjectType::from([
692                (
693                    "nested".to_string(),
694                    JsonNativeType::Object(JsonObjectType::from([(
695                        "a".to_string(),
696                        JsonNativeType::String,
697                    )])),
698                ),
699                ("bar".to_string(), JsonNativeType::i64()),
700            ])),
701            false,
702        );
703        test(
704            complex_json_object,
705            &JsonNativeType::Object(JsonObjectType::from([
706                (
707                    "nested".to_string(),
708                    JsonNativeType::Object(JsonObjectType::from([(
709                        "a".to_string(),
710                        JsonNativeType::Object(JsonObjectType::from([(
711                            "b".to_string(),
712                            JsonNativeType::String,
713                        )])),
714                    )])),
715                ),
716                ("bar".to_string(), JsonNativeType::i64()),
717            ])),
718            false,
719        );
720        test(
721            complex_json_object,
722            &JsonNativeType::Object(JsonObjectType::from([
723                (
724                    "nested".to_string(),
725                    JsonNativeType::Object(JsonObjectType::from([(
726                        "a".to_string(),
727                        JsonNativeType::Object(JsonObjectType::from([(
728                            "b".to_string(),
729                            JsonNativeType::Object(JsonObjectType::from([(
730                                "c".to_string(),
731                                JsonNativeType::Null,
732                            )])),
733                        )])),
734                    )])),
735                ),
736                ("bar".to_string(), JsonNativeType::i64()),
737            ])),
738            true,
739        );
740        test(
741            complex_json_object,
742            &JsonNativeType::Object(JsonObjectType::from([
743                (
744                    "nested".to_string(),
745                    JsonNativeType::Object(JsonObjectType::from([(
746                        "a".to_string(),
747                        JsonNativeType::Object(JsonObjectType::from([(
748                            "b".to_string(),
749                            JsonNativeType::Object(JsonObjectType::from([(
750                                "c".to_string(),
751                                JsonNativeType::Bool,
752                            )])),
753                        )])),
754                    )])),
755                ),
756                ("bar".to_string(), JsonNativeType::i64()),
757            ])),
758            false,
759        );
760        test(
761            complex_json_object,
762            &JsonNativeType::Object(JsonObjectType::from([(
763                "nested".to_string(),
764                JsonNativeType::Object(JsonObjectType::from([(
765                    "a".to_string(),
766                    JsonNativeType::Object(JsonObjectType::from([(
767                        "b".to_string(),
768                        JsonNativeType::Object(JsonObjectType::from([(
769                            "c".to_string(),
770                            JsonNativeType::String,
771                        )])),
772                    )])),
773                )])),
774            )])),
775            true,
776        );
777    }
778
779    #[test]
780    fn test_merge_json_type() {
781        fn test(other: JsonNativeType, json_type: &mut JsonNativeType, expected: &str) {
782            json_type.merge(&other);
783            assert_eq!(json_type.to_string(), expected);
784        }
785
786        // Null should be absorbed by a concrete scalar type.
787        test(
788            JsonNativeType::Bool,
789            &mut JsonNativeType::Null,
790            r#""<Bool>""#,
791        );
792
793        // Merging a null value into an existing concrete type should keep the type unchanged.
794        test(
795            JsonNativeType::Null,
796            &mut JsonNativeType::Bool,
797            r#""<Bool>""#,
798        );
799
800        // Identical number categories should stay as Number.
801        test(
802            JsonNativeType::i64(),
803            &mut JsonNativeType::i64(),
804            r#""<Number>""#,
805        );
806
807        // Conflicting number categories should be lifted to Variant.
808        for (mut this, other) in [
809            (JsonNativeType::u64(), JsonNativeType::i64()),
810            (JsonNativeType::u64(), JsonNativeType::f64()),
811            (JsonNativeType::i64(), JsonNativeType::f64()),
812        ] {
813            test(other, &mut this, r#""<Variant>""#);
814        }
815
816        // Object merge should preserve existing fields and append missing fields.
817        test(
818            JsonNativeType::Object(JsonObjectType::from([(
819                "foo".to_string(),
820                JsonNativeType::String,
821            )])),
822            &mut JsonNativeType::Object(JsonObjectType::from([(
823                "bar".to_string(),
824                JsonNativeType::i64(),
825            )])),
826            r#"{"bar":"<Number>","foo":"<String>"}"#,
827        );
828
829        // Conflicting object field types should only lift that field to Variant.
830        test(
831            JsonNativeType::Object(JsonObjectType::from([(
832                "foo".to_string(),
833                JsonNativeType::i64(),
834            )])),
835            &mut JsonNativeType::Object(JsonObjectType::from([(
836                "foo".to_string(),
837                JsonNativeType::Bool,
838            )])),
839            r#"{"foo":"<Variant>"}"#,
840        );
841
842        // Nested objects should merge recursively.
843        test(
844            JsonNativeType::Object(JsonObjectType::from([(
845                "nested".to_string(),
846                JsonNativeType::Object(JsonObjectType::from([(
847                    "foo".to_string(),
848                    JsonNativeType::String,
849                )])),
850            )])),
851            &mut JsonNativeType::Object(JsonObjectType::from([(
852                "nested".to_string(),
853                JsonNativeType::Object(JsonObjectType::from([(
854                    "bar".to_string(),
855                    JsonNativeType::Bool,
856                )])),
857            )])),
858            r#"{"nested":{"bar":"<Bool>","foo":"<String>"}}"#,
859        );
860
861        // Arrays should merge their element types recursively.
862        test(
863            JsonNativeType::Array(Box::new(JsonNativeType::String)),
864            &mut JsonNativeType::Array(Box::new(JsonNativeType::u64())),
865            r#"["<Variant>"]"#,
866        );
867
868        // Root-level incompatible types should be lifted to Variant.
869        test(
870            JsonNativeType::Object(JsonObjectType::from([(
871                "foo".to_string(),
872                JsonNativeType::String,
873            )])),
874            &mut JsonNativeType::Bool,
875            r#""<Variant>""#,
876        );
877    }
878}