This is an automated email from the ASF dual-hosted git repository. kriskras99 pushed a commit to branch feat/enums in repository https://gitbox.apache.org/repos/asf/avro-rs.git
commit c1adfee11f728a56c04fefc30c4916bca3f77d24 Author: default <[email protected]> AuthorDate: Wed Mar 4 16:05:37 2026 +0000 Add test suite for deser --- avro/src/reader/datum.rs | 6 +- avro/src/serde/deser_schema/mod.rs | 954 ++++++++++++++++++++++++++++++++++++- 2 files changed, 954 insertions(+), 6 deletions(-) diff --git a/avro/src/reader/datum.rs b/avro/src/reader/datum.rs index f72b272..9eeb78e 100644 --- a/avro/src/reader/datum.rs +++ b/avro/src/reader/datum.rs @@ -135,7 +135,7 @@ impl<'s> GenericDatumReader<'s> { } } - pub fn read_deser<R: Read, T: DeserializeOwned>(&self, reader: &mut R) -> AvroResult<T> { + pub fn read_deser<T: DeserializeOwned>(&self, reader: &mut impl Read) -> AvroResult<T> { T::deserialize(SchemaAwareDeserializer::new( reader, self.writer, @@ -352,11 +352,11 @@ mod tests { } let schema = Schema::parse_str(TEST_RECORD_SCHEMA_3240)?; - let mut encoded: &'static [u8] = &[54, 6, 102, 111, 111]; + let mut encoded: &[u8] = &[54, 6, 102, 111, 111]; let error = GenericDatumReader::builder(&schema) .build()? - .read_deser::<_, TestRecord3240>(&mut encoded) + .read_deser::<TestRecord3240>(&mut encoded) .unwrap_err(); assert_eq!( diff --git a/avro/src/serde/deser_schema/mod.rs b/avro/src/serde/deser_schema/mod.rs index fc6c560..7e61686 100644 --- a/avro/src/serde/deser_schema/mod.rs +++ b/avro/src/serde/deser_schema/mod.rs @@ -182,8 +182,8 @@ impl<'s, 'r, R: Read, S: Borrow<Schema>> SchemaAwareDeserializer<'s, 'r, R, S> { } } -const DESERIALIZE_ANY: &str = "This value is compared by pointer value"; -const DESERIALIZE_ANY_FIELDS: &[&str] = &[]; +static DESERIALIZE_ANY: &str = "This value is compared by pointer value"; +static DESERIALIZE_ANY_FIELDS: &[&str] = &[]; impl<'de, 's, 'r, R: Read, S: Borrow<Schema>> Deserializer<'de> for SchemaAwareDeserializer<'s, 'r, R, S> @@ -447,7 +447,7 @@ impl<'de, 's, 'r, R: Read, S: Borrow<Schema>> Deserializer<'de> V: serde::de::Visitor<'de>, { match self.schema { - Schema::String => { + Schema::String | Schema::Uuid(UuidSchema::String) => { let string = self.read_string()?; visitor.visit_string(string) } @@ -727,3 +727,951 @@ impl<'de, 's, 'r, R: Read, S: Borrow<Schema>> Deserializer<'de> self.deserialize_any(visitor) } } + +#[cfg(test)] +mod tests { + use std::io::Cursor; + + use num_bigint::BigInt; + use pretty_assertions::assert_eq; + use serde::de::{DeserializeOwned, Visitor}; + use serde::{Deserialize, Serialize}; + use uuid::Uuid; + + use apache_avro_test_helper::TestResult; + + use super::*; + use crate::reader::datum::GenericDatumReader; + use crate::writer::datum::GenericDatumWriter; + use crate::{AvroResult, Decimal}; + + #[track_caller] + fn assert_roundtrip<T>(value: T, schema: &Schema, schemata: Vec<&Schema>) -> AvroResult<()> + where + T: Serialize + DeserializeOwned + PartialEq + Debug + Clone, + { + let buf = GenericDatumWriter::builder(schema) + .schemata(schemata.clone())? + .build()? + .write_ser_to_vec(&value)?; + + let decoded_value: T = GenericDatumReader::builder(schema) + .writer_schemata(schemata)? + .build()? + .read_deser(&mut Cursor::new(buf))?; + + assert_eq!(decoded_value, value); + + Ok(()) + } + + #[test] + fn avro_3955_decode_enum() -> TestResult { + #[derive(Debug, Serialize, Deserialize, Clone, PartialEq)] + #[serde(rename_all = "SCREAMING_SNAKE_CASE")] + pub enum SourceType { + Sozu, + Haproxy, + HaproxyTcp, + } + #[derive(Debug, Serialize, Deserialize, Clone, PartialEq)] + struct AccessLog { + source: SourceType, + } + + let schema = Schema::parse_str( + r#"{ + "name": "AccessLog", + "namespace": "com.clevercloud.accesslogs.common.avro", + "type": "record", + "fields": [{ + "name": "source", + "type": { + "type": "enum", + "name": "SourceType", + "items": "string", + "symbols": ["SOZU", "HAPROXY", "HAPROXY_TCP"] + } + }] + }"#, + )?; + + let data = AccessLog { + source: SourceType::Sozu, + }; + + assert_roundtrip(data, &schema, Vec::new())?; + + Ok(()) + } + + #[test] + fn avro_rs_xxx_decode_enum_invalid_data() -> TestResult { + #[derive(Debug, Serialize, Deserialize, Clone, PartialEq)] + #[serde(rename_all = "SCREAMING_SNAKE_CASE")] + pub enum SourceType { + Sozu, + Haproxy, + HaproxyTcp, + } + #[derive(Debug, Serialize, Deserialize, Clone, PartialEq)] + struct AccessLog { + source: SourceType, + } + + let schema = Schema::parse_str( + r#"{ + "name": "AccessLog", + "namespace": "com.clevercloud.accesslogs.common.avro", + "type": "record", + "fields": [{ + "name": "source", + "type": { + "type": "enum", + "name": "SourceType", + "items": "string", + "symbols": ["SOZU", "HAPROXY", "HAPROXY_TCP"] + } + }] + }"#, + )?; + + // Contains index 3 (4th symbol) + let data_with_unknown_index = vec![6u8]; + + let error = GenericDatumReader::builder(&schema) + .build()? + .read_deser::<AccessLog>(&mut Cursor::new(data_with_unknown_index)) + .unwrap_err(); + + assert_eq!(error.to_string(), "Enum symbol index out of bounds: 3"); + + Ok(()) + } + + #[test] + fn avro_rs_xxx_nested_struct() -> TestResult { + #[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq)] + struct Test { + a: i64, + b: String, + c: Decimal, + } + + #[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq)] + struct TestInner { + a: Test, + b: i32, + } + + let schemas = Schema::parse_list([ + r#"{ + "name": "Test", + "type": "record", + "fields": [ + { + "name": "a", + "type": "long" + }, + { + "name": "b", + "type": "string" + }, + { + "name": "c", + "type": { + "type": "bytes", + "logicalType": "decimal", + "precision": 4, + "scale": 2 + } + } + ] + }"#, + r#"{ + "name": "TestInner", + "type": "record", + "fields": [ + { + "name": "a", + "type": "Test" + }, + { + "name": "b", + "type": "int" + } + ] + }"#, + ])?; + + let test = Test { + a: 27, + b: "foo".to_string(), + c: Decimal::from(vec![1, 24]), + }; + + assert_roundtrip(test.clone(), &schemas[0], Vec::new())?; + + let test_inner = TestInner { a: test, b: 35 }; + + assert_roundtrip(test_inner, &schemas[1], vec![&schemas[0]])?; + + Ok(()) + } + + #[test] + fn avro_rs_xxx_external_unit_enum() -> TestResult { + #[derive(Debug, Serialize, Deserialize, Clone, PartialEq)] + pub enum UnitExternalEnum { + Val1, + Val2, + } + #[derive(Debug, Serialize, Deserialize, Clone, PartialEq)] + struct TestUnitExternalEnum { + a: UnitExternalEnum, + } + + let schema = Schema::parse_str( + r#"{ + "name": "TestUnitExternalEnum", + "type": "record", + "fields": [{ + "name": "a", + "type": { + "type": "enum", + "name": "UnitExternalEnum", + "items": "string", + "symbols": ["Val1", "Val2"] + } + }] + }"#, + )?; + + let alt_schema = Schema::parse_str( + r#"{ + "name": "TestUnitExternalEnum", + "type": "record", + "fields": [{ + "name": "a", + "type": [ + { + "name": "Val1", + "type": "record", + "fields": [] + }, + { + "name": "Val2", + "type": "record", + "fields": [] + } + ] + }] + }"#, + )?; + + let value = TestUnitExternalEnum { + a: UnitExternalEnum::Val1, + }; + assert_roundtrip(value.clone(), &schema, Vec::new())?; + assert_roundtrip(value, &alt_schema, Vec::new())?; + + let value = TestUnitExternalEnum { + a: UnitExternalEnum::Val2, + }; + assert_roundtrip(value.clone(), &alt_schema, Vec::new())?; + assert_roundtrip(value, &schema, Vec::new())?; + + Ok(()) + } + + #[test] + fn avro_rs_xxx_internal_unit_enum() -> TestResult { + #[derive(Debug, Serialize, Deserialize, Clone, PartialEq)] + #[serde(tag = "t")] + pub enum UnitInternalEnum { + Val1, + Val2, + } + #[derive(Debug, Serialize, Deserialize, Clone, PartialEq)] + struct TestUnitInternalEnum { + a: UnitInternalEnum, + } + + let schema = Schema::parse_str( + r#"{ + "name": "TestUnitInternalEnum", + "type": "record", + "fields": [{ + "name": "a", + "type": { + "type": "record", + "name": "UnitInternalEnum", + "fields": [{ + "name": "t", + "type": { + "type": "enum", + "name": "t", + "symbols": ["Val1", "Val2"] + } + }] + } + }] + }"#, + )?; + + let value = TestUnitInternalEnum { + a: UnitInternalEnum::Val1, + }; + assert_roundtrip(value, &schema, Vec::new())?; + + let value = TestUnitInternalEnum { + a: UnitInternalEnum::Val2, + }; + assert_roundtrip(value, &schema, Vec::new())?; + + Ok(()) + } + + #[test] + fn avro_rs_xxx_adjacent_unit_enum() -> TestResult { + #[derive(Debug, Serialize, Deserialize, Clone, PartialEq)] + #[serde(tag = "t", content = "v")] + pub enum UnitAdjacentEnum { + Val1, + Val2, + } + #[derive(Debug, Serialize, Deserialize, Clone, PartialEq)] + struct TestUnitAdjacentEnum { + a: UnitAdjacentEnum, + } + + let schema = Schema::parse_str( + r#"{ + "name": "TestUnitAdjacentEnum", + "type": "record", + "fields": [{ + "name": "a", + "type": { + "type": "record", + "name": "UnitAdjacentEnum", + "fields": [ + { + "name": "t", + "type": { + "type": "enum", + "name": "t", + "symbols": ["Val1", "Val2"] + } + }, + { + "name": "v", + "default": null, + "type": ["null"] + } + ] + } + }] + }"#, + )?; + + let value = TestUnitAdjacentEnum { + a: UnitAdjacentEnum::Val1, + }; + assert_roundtrip(value, &schema, Vec::new())?; + + let value = TestUnitAdjacentEnum { + a: UnitAdjacentEnum::Val2, + }; + assert_roundtrip(value, &schema, Vec::new())?; + + Ok(()) + } + + #[test] + fn avro_rs_xxx_untagged_unit_enum() -> TestResult { + #[derive(Debug, Serialize, Deserialize, Clone, PartialEq)] + #[serde(untagged)] + pub enum UnitUntaggedEnum { + Val1, + Val2, + } + #[derive(Debug, Serialize, Deserialize, Clone, PartialEq)] + struct TestUnitUntaggedEnum { + a: UnitUntaggedEnum, + } + + let schema = Schema::parse_str( + r#"{ + "name": "TestUnitUntaggedEnum", + "type": "record", + "fields": [{ + "name": "a", + "type": ["null"] + }] + }"#, + )?; + + let value1 = TestUnitUntaggedEnum { + a: UnitUntaggedEnum::Val1, + }; + assert_roundtrip(value1.clone(), &schema, Vec::new())?; + + let value2 = TestUnitUntaggedEnum { + a: UnitUntaggedEnum::Val2, + }; + let buf = GenericDatumWriter::builder(&schema) + .build()? + .write_ser_to_vec(&value1)?; + + let decoded_value: TestUnitUntaggedEnum = GenericDatumReader::builder(&schema) + .build()? + .read_deser(&mut Cursor::new(buf))?; + + // Val2 cannot troundtrip. All unit variants are serialized to the same null. + // This also doesn't roundtrip in serde_json. + assert_ne!(value2, decoded_value); + assert_eq!(decoded_value, value1); + + Ok(()) + } + + #[test] + fn avro_rs_xxx_mixed_enum() -> TestResult { + #[derive(Debug, Deserialize, Serialize, Clone, PartialEq)] + struct TestNullExternalEnum { + a: NullExternalEnum, + } + + #[derive(Debug, Deserialize, Serialize, Clone, PartialEq)] + enum NullExternalEnum { + Val1, + Val2(), + Val3(()), + Val4(u64), + } + + let schema = Schema::parse_str( + r#"{ + "name": "TestNullExternalEnum", + "type": "record", + "fields": [{ + "name": "a", + "type": [ + { + "name": "Val1", + "type": "record", + "fields": [] + }, + { + "name": "Val2", + "type": "record", + "fields": [] + }, + { + "name": "Val3", + "type": "record", + "fields": [{ + "name": "field_0", + "type": "null" + }] + }, + { + "name": "Val4", + "type": "record", + "fields": [{ + "name": "field_0", + "type": { + "type": "fixed", + "name": "u64", + "size": 8 + } + }] + } + ] + }] + }"#, + )?; + + let data = [ + TestNullExternalEnum { + a: NullExternalEnum::Val1, + }, + TestNullExternalEnum { + a: NullExternalEnum::Val2(), + }, + TestNullExternalEnum { + a: NullExternalEnum::Val2(), + }, + TestNullExternalEnum { + a: NullExternalEnum::Val3(()), + }, + TestNullExternalEnum { + a: NullExternalEnum::Val4(123), + }, + ]; + + for value in data { + assert_roundtrip(value, &schema, Vec::new())?; + } + + Ok(()) + } + + #[test] + fn avro_rs_xxx_single_value_enum() -> TestResult { + #[derive(Debug, Serialize, Deserialize, Clone, PartialEq)] + struct TestSingleValueExternalEnum { + a: SingleValueExternalEnum, + } + + #[derive(Debug, Serialize, Deserialize, Clone, PartialEq)] + enum SingleValueExternalEnum { + Double(f64), + String(String), + } + + let schema = Schema::parse_str( + r#"{ + "name": "TestSingleValueExternalEnum", + "type": "record", + "fields": [{ + "name": "a", + "type": [ + { + "name": "Val1", + "type": "record", + "fields": [{ + "name": "field_0", + "type": "double" + }] + }, + { + "name": "Val2", + "type": "record", + "fields": [{ + "name": "field_0", + "type": "string" + }] + } + ] + }] + }"#, + )?; + + let alt_schema = Schema::parse_str( + r#"{ + "name": "TestSingleValueExternalEnum", + "type": "record", + "fields": [{ + "name": "a", + "type": ["double", "string"] + }] + }"#, + )?; + + let double = TestSingleValueExternalEnum { + a: SingleValueExternalEnum::Double(64.0), + }; + assert_roundtrip(double.clone(), &schema, Vec::new())?; + assert_roundtrip(double, &alt_schema, Vec::new())?; + + let string = TestSingleValueExternalEnum { + a: SingleValueExternalEnum::String("test".to_string()), + }; + assert_roundtrip(string.clone(), &schema, Vec::new())?; + assert_roundtrip(string, &alt_schema, Vec::new())?; + + Ok(()) + } + + #[test] + fn avro_rs_xxx_struct_enum() -> TestResult { + #[derive(Debug, Serialize, Deserialize, Clone, PartialEq)] + struct TestStructExternalEnum { + a: StructExternalEnum, + } + + #[derive(Debug, Serialize, Deserialize, Clone, PartialEq)] + enum StructExternalEnum { + Val1 { x: f32, y: f32 }, + Val2 { x: f32, y: f32 }, + } + + let schema = Schema::parse_str( + r#"{ + "name": "TestStructExternalEnum", + "type": "record", + "fields": [{ + "name": "a", + "type": [ + { + "name": "Val1", + "type": "record", + "fields": [ + { + "name": "x", + "type": "float" + }, + { + "name": "y", + "type": "float" + } + ] + }, + { + "name": "Val2", + "type": "record", + "fields": [ + { + "name": "x", + "type": "float" + }, + { + "name": "y", + "type": "float" + } + ] + } + ] + }] + }"#, + )?; + + let value1 = TestStructExternalEnum { + a: StructExternalEnum::Val1 { x: 1.0, y: 2.0 }, + }; + + assert_roundtrip(value1, &schema, Vec::new())?; + + let value2 = TestStructExternalEnum { + a: StructExternalEnum::Val2 { x: 2.0, y: 1.0 }, + }; + + assert_roundtrip(value2, &schema, Vec::new())?; + + Ok(()) + } + + #[test] + fn avro_rs_xxx_struct_flatten() -> TestResult { + #[derive(Debug, Serialize, Deserialize, Clone, PartialEq)] + struct S1 { + f1: String, + #[serde(flatten)] + inner: S2, + } + + #[derive(Debug, Serialize, Deserialize, Clone, PartialEq)] + struct S2 { + f2: String, + } + + let schema = Schema::parse_str( + r#"{ + "name": "S1", + "type": "record", + "fields": [ + { + "name": "f1", + "type": "string" + }, + { + "name": "f2", + "type": "string" + } + ] + }"#, + )?; + + let value = S1 { + f1: "Hello".to_owned(), + inner: S2 { + f2: "World".to_owned(), + }, + }; + + assert_roundtrip(value, &schema, Vec::new())?; + + Ok(()) + } + + #[test] + fn avro_rs_xxx_tuple_enum() -> TestResult { + #[derive(Debug, Serialize, Deserialize, Clone, PartialEq)] + struct TestTupleExternalEnum { + a: TupleExternalEnum, + } + + #[derive(Debug, Serialize, Deserialize, Clone, PartialEq)] + enum TupleExternalEnum { + Val1(f32, f32), + Val2(f32, f32, f32), + } + + let schema = Schema::parse_str( + r#"{ + "name": "TestTupleExternalEnum", + "type": "record", + "fields": [{ + "name": "a", + "type": [ + { + "name": "Val1", + "type": "record", + "fields": [ + { + "name": "field_0", + "type": "float" + }, + { + "name": "field_1", + "type": "float" + } + ] + }, + { + "name": "Val2", + "type": "record", + "fields": [ + { + "name": "field_0", + "type": "float" + }, + { + "name": "field_1", + "type": "float" + }, + { + "name": "field_2", + "type": "float" + } + ] + } + ] + }] + }"#, + )?; + + let value1 = TestTupleExternalEnum { + a: TupleExternalEnum::Val1(1.0, 2.0), + }; + + assert_roundtrip(value1, &schema, Vec::new())?; + + let value2 = TestTupleExternalEnum { + a: TupleExternalEnum::Val1(2.0, 1.0), + }; + + assert_roundtrip(value2, &schema, Vec::new())?; + + Ok(()) + } + + #[test] + fn avro_rs_xxx_date() -> TestResult { + let schema = Schema::Date; + assert_roundtrip(1i32, &schema, Vec::new())?; + + Ok(()) + } + + #[test] + fn avro_rs_xxx_time_millis() -> TestResult { + let schema = Schema::TimeMillis; + assert_roundtrip(1i32, &schema, Vec::new())?; + + Ok(()) + } + + #[test] + fn avro_rs_xxx_time_micros() -> TestResult { + let schema = Schema::TimeMicros; + assert_roundtrip(1i64, &schema, Vec::new())?; + + Ok(()) + } + + #[test] + fn avro_rs_xxx_timestamp_millis() -> TestResult { + let schema = Schema::TimestampMillis; + assert_roundtrip(1i64, &schema, Vec::new())?; + + Ok(()) + } + + #[test] + fn avro_rs_xxx_timestamp_micros() -> TestResult { + let schema = Schema::TimestampMicros; + assert_roundtrip(1i64, &schema, Vec::new())?; + + Ok(()) + } + + #[test] + fn avro_3916_timestamp_nanos() -> TestResult { + let schema = Schema::TimestampNanos; + assert_roundtrip(1i64, &schema, Vec::new())?; + + Ok(()) + } + + #[test] + fn avro_3853_local_timestamp_millis() -> TestResult { + let schema = Schema::LocalTimestampMillis; + assert_roundtrip(1i64, &schema, Vec::new())?; + + Ok(()) + } + + #[test] + fn avro_3853_local_timestamp_micros() -> TestResult { + let schema = Schema::LocalTimestampMicros; + assert_roundtrip(1i64, &schema, Vec::new())?; + + Ok(()) + } + + #[test] + fn avro_3916_local_timestamp_nanos() -> TestResult { + let schema = Schema::LocalTimestampNanos; + assert_roundtrip(1i64, &schema, Vec::new())?; + + Ok(()) + } + + #[test] + fn avro_rs_xxx_uuid() -> TestResult { + let schema = Schema::parse_str( + r#"{ + "type": "fixed", + "logicalType": "uuid", + "size": 16, + "name": "uuid" + }"#, + )?; + + let alt_schema = Schema::Uuid(UuidSchema::String); + + let uuid = Uuid::parse_str("9ec535ff-3e2a-45bd-91d3-0a01321b5a49")?; + + assert_roundtrip(uuid, &schema, Vec::new())?; + + let buf = GenericDatumWriter::builder(&alt_schema) + .human_readable(true) + .build()? + .write_ser_to_vec(&uuid)?; + + let decoded_value: Uuid = GenericDatumReader::builder(&alt_schema) + .human_readable(true) + .build()? + .read_deser(&mut Cursor::new(buf))?; + + assert_eq!(decoded_value, uuid); + + Ok(()) + } + + #[derive(Debug)] + struct Bytes(Vec<u8>); + + impl<'de> Deserialize<'de> for Bytes { + fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> + where + D: serde::Deserializer<'de>, + { + struct BytesVisitor; + impl Visitor<'_> for BytesVisitor { + type Value = Bytes; + + fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { + formatter.write_str("a byte array") + } + + fn visit_bytes<E>(self, v: &[u8]) -> Result<Self::Value, E> + where + E: serde::de::Error, + { + Ok(Bytes(v.to_vec())) + } + } + deserializer.deserialize_bytes(BytesVisitor) + } + } + + #[test] + fn avro_3892_deserialize_bytes_from_decimal() -> TestResult { + let schema = Schema::parse_str( + r#"{ + "type": "bytes", + "logicalType": "decimal", + "precision": 4, + "scale": 2 + }"#, + )?; + let schema_union = Schema::parse_str( + r#"[ + "null", + { + "type": "bytes", + "logicalType": "decimal", + "precision": 4, + "scale": 2 + } + ]"#, + )?; + + let expected_bytes = BigInt::from(123456789).to_signed_bytes_be(); + let value = Decimal::from(&expected_bytes); + let buf = GenericDatumWriter::builder(&schema) + .build()? + .write_ser_to_vec(&value)?; + + let decoded_value: Bytes = GenericDatumReader::builder(&schema) + .build()? + .read_deser(&mut Cursor::new(buf))?; + + assert_eq!(decoded_value.0, expected_bytes); + + let buf = GenericDatumWriter::builder(&schema_union) + .build()? + .write_ser_to_vec(&Some(value))?; + + let decoded_value: Option<Bytes> = GenericDatumReader::builder(&schema_union) + .build()? + .read_deser(&mut Cursor::new(buf))?; + + assert_eq!(decoded_value.unwrap().0, expected_bytes); + + Ok(()) + } + + #[test] + fn avro_rs_414_deserialize_char_from_string() -> TestResult { + let schema = Schema::String; + + assert_roundtrip('a', &schema, Vec::new())?; + assert_roundtrip('👹', &schema, Vec::new())?; + + Ok(()) + } + + #[test] + fn avro_rs_414_deserialize_char_from_long_string() -> TestResult { + let schema = Schema::String; + let buf = GenericDatumWriter::builder(&schema) + .build()? + .write_ser_to_vec(&"avro")?; + + let error = GenericDatumReader::builder(&schema) + .build()? + .read_deser::<char>(&mut Cursor::new(buf)) + .unwrap_err(); + + assert_eq!( + error.to_string(), + "Failed to deserialize value of type char using schema String: String contains more than one character" + ); + + Ok(()) + } +}
