Skip to main content

arrow_schema/extension/canonical/
json.rs

1// SPDX-License-Identifier: Apache-2.0
2// SPDX-FileCopyrightText: See the NOTICE file distributed with this work for additional information regarding copyright ownership
3// SPDX-FileContributor: Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements
4
5//! JSON
6//!
7//! <https://arrow.apache.org/docs/format/CanonicalExtensions.html#json>
8
9use serde_core::de::{self, MapAccess, Visitor};
10use serde_core::ser::SerializeStruct;
11use serde_core::{Deserialize, Deserializer, Serialize, Serializer};
12use std::fmt;
13
14use crate::{ArrowError, DataType, extension::ExtensionType};
15
16/// The extension type for `JSON`.
17///
18/// Extension name: `arrow.json`.
19///
20/// The storage type of this extension is `String` or `LargeString` or
21/// `StringView`. Only UTF-8 encoded JSON as specified in [rfc8259](https://datatracker.ietf.org/doc/html/rfc8259)
22/// is supported.
23///
24/// This type does not have any parameters.
25///
26/// Metadata is either an empty string or a JSON string with an empty
27/// object. In the future, additional fields may be added, but they are not
28/// required to interpret the array.
29///
30/// <https://arrow.apache.org/docs/format/CanonicalExtensions.html#json>
31#[derive(Debug, Clone, Default, PartialEq)]
32pub struct Json(JsonMetadata);
33
34/// Empty object
35#[derive(Debug, Clone, Copy, PartialEq)]
36struct Empty {}
37
38impl Serialize for Empty {
39    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
40    where
41        S: Serializer,
42    {
43        let state = serializer.serialize_struct("Empty", 0)?;
44        state.end()
45    }
46}
47
48struct EmptyVisitor;
49
50impl<'de> Visitor<'de> for EmptyVisitor {
51    type Value = Empty;
52
53    fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
54        formatter.write_str("struct Empty")
55    }
56
57    fn visit_seq<A>(self, mut _seq: A) -> Result<Self::Value, A::Error>
58    where
59        A: de::SeqAccess<'de>,
60    {
61        Ok(Empty {})
62    }
63
64    fn visit_map<V>(self, mut map: V) -> Result<Empty, V::Error>
65    where
66        V: MapAccess<'de>,
67    {
68        if let Some(key) = map.next_key::<String>()? {
69            return Err(de::Error::unknown_field(&key, EMPTY_FIELDS));
70        }
71        Ok(Empty {})
72    }
73
74    fn visit_u64<E>(self, _v: u64) -> Result<Self::Value, E>
75    where
76        E: de::Error,
77    {
78        Err(de::Error::unknown_field("", EMPTY_FIELDS))
79    }
80
81    fn visit_str<E>(self, _v: &str) -> Result<Self::Value, E>
82    where
83        E: de::Error,
84    {
85        Err(de::Error::unknown_field("", EMPTY_FIELDS))
86    }
87
88    fn visit_bytes<E>(self, _v: &[u8]) -> Result<Self::Value, E>
89    where
90        E: de::Error,
91    {
92        Err(de::Error::unknown_field("", EMPTY_FIELDS))
93    }
94}
95
96static EMPTY_FIELDS: &[&str] = &[];
97
98impl<'de> Deserialize<'de> for Empty {
99    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
100    where
101        D: Deserializer<'de>,
102    {
103        deserializer.deserialize_struct("Empty", EMPTY_FIELDS, EmptyVisitor)
104    }
105}
106
107/// Extension type metadata for [`Json`].
108#[derive(Debug, Default, Clone, PartialEq)]
109pub struct JsonMetadata(Option<Empty>);
110
111impl ExtensionType for Json {
112    const NAME: &'static str = "arrow.json";
113
114    type Metadata = JsonMetadata;
115
116    fn metadata(&self) -> &Self::Metadata {
117        &self.0
118    }
119
120    fn serialize_metadata(&self) -> Option<String> {
121        Some(
122            self.metadata()
123                .0
124                .as_ref()
125                .map(serde_json::to_string)
126                .map(Result::unwrap)
127                .unwrap_or_default(),
128        )
129    }
130
131    fn deserialize_metadata(metadata: Option<&str>) -> Result<Self::Metadata, ArrowError> {
132        const ERR: &str = "Json extension type metadata is either an empty string or a JSON string with an empty object";
133        metadata
134            .map_or_else(
135                || Err(ArrowError::InvalidArgumentError(ERR.to_owned())),
136                |metadata| {
137                    match metadata {
138                        // Empty string
139                        "" => Ok(None),
140                        value => serde_json::from_str::<Empty>(value)
141                            .map(Option::Some)
142                            .map_err(|_| ArrowError::InvalidArgumentError(ERR.to_owned())),
143                    }
144                },
145            )
146            .map(JsonMetadata)
147    }
148
149    fn supports_data_type(&self, data_type: &DataType) -> Result<(), ArrowError> {
150        match data_type {
151            DataType::Utf8 | DataType::LargeUtf8 | DataType::Utf8View => Ok(()),
152            data_type => Err(ArrowError::InvalidArgumentError(format!(
153                "Json data type mismatch, expected one of Utf8, LargeUtf8, Utf8View, found {data_type}"
154            ))),
155        }
156    }
157
158    fn try_new(data_type: &DataType, metadata: Self::Metadata) -> Result<Self, ArrowError> {
159        let json = Self(metadata);
160        json.supports_data_type(data_type)?;
161        Ok(json)
162    }
163
164    fn validate(data_type: &DataType, _metadata: Self::Metadata) -> Result<(), ArrowError> {
165        Self::default().supports_data_type(data_type)
166    }
167}
168
169#[cfg(test)]
170mod tests {
171    #[cfg(feature = "canonical_extension_types")]
172    use crate::extension::CanonicalExtensionType;
173    use crate::{
174        Field,
175        extension::{EXTENSION_TYPE_METADATA_KEY, EXTENSION_TYPE_NAME_KEY},
176    };
177
178    use super::*;
179
180    #[test]
181    fn valid() -> Result<(), ArrowError> {
182        let mut field = Field::new("", DataType::Utf8, false);
183        field.try_with_extension_type(Json::default())?;
184        assert_eq!(
185            field.metadata().get(EXTENSION_TYPE_METADATA_KEY),
186            Some(&String::new())
187        );
188        assert_eq!(
189            field.try_extension_type::<Json>()?,
190            Json(JsonMetadata(None))
191        );
192
193        let mut field = Field::new("", DataType::LargeUtf8, false);
194        field.try_with_extension_type(Json(JsonMetadata(Some(Empty {}))))?;
195        assert_eq!(
196            field.metadata().get(EXTENSION_TYPE_METADATA_KEY),
197            Some(&"{}".to_owned())
198        );
199        assert_eq!(
200            field.try_extension_type::<Json>()?,
201            Json(JsonMetadata(Some(Empty {})))
202        );
203
204        let mut field = Field::new("", DataType::Utf8View, false);
205        field.try_with_extension_type(Json::default())?;
206        field.try_extension_type::<Json>()?;
207        #[cfg(feature = "canonical_extension_types")]
208        assert_eq!(
209            field.try_canonical_extension_type()?,
210            CanonicalExtensionType::Json(Json::default())
211        );
212        Ok(())
213    }
214
215    #[test]
216    #[should_panic(expected = "Extension type name missing")]
217    fn missing_name() {
218        let field = Field::new("", DataType::Int8, false)
219            .with_metadata([(EXTENSION_TYPE_METADATA_KEY, "{}")]);
220        field.extension_type::<Json>();
221    }
222
223    #[test]
224    #[should_panic(expected = "expected one of Utf8, LargeUtf8, Utf8View, found Null")]
225    fn invalid_type() {
226        Field::new("", DataType::Null, false).with_extension_type(Json::default());
227    }
228
229    #[test]
230    #[should_panic(
231        expected = "Json extension type metadata is either an empty string or a JSON string with an empty object"
232    )]
233    fn invalid_metadata() {
234        let field = Field::new("", DataType::Utf8, false).with_metadata([
235            (EXTENSION_TYPE_NAME_KEY, Json::NAME),
236            (EXTENSION_TYPE_METADATA_KEY, "1234"),
237        ]);
238        field.extension_type::<Json>();
239    }
240
241    #[test]
242    #[should_panic(
243        expected = "Json extension type metadata is either an empty string or a JSON string with an empty object"
244    )]
245    fn missing_metadata() {
246        let field = Field::new("", DataType::LargeUtf8, false)
247            .with_metadata([(EXTENSION_TYPE_NAME_KEY, Json::NAME)]);
248        field.extension_type::<Json>();
249    }
250}