From 968f9fab800598b13bcba2a5435f89a91ed64467 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E6=9F=84=E6=B6=B5?= Date: Fri, 28 Jul 2023 15:53:15 +0800 Subject: [PATCH] =?UTF-8?q?=E3=80=90ylong=5Fjson=E3=80=91=E4=BF=AE?= =?UTF-8?q?=E5=A4=8D=20serde=20adapt=20=E5=8F=8D=E5=BA=8F=E5=88=97?= =?UTF-8?q?=E8=A7=A3=E6=9E=90=20objcect=20=E9=80=BB=E8=BE=91=E9=97=AE?= =?UTF-8?q?=E9=A2=98=20&=20=E8=A1=A5=E5=85=85=20serde=20adapt=20=E5=8F=8D?= =?UTF-8?q?=E5=BA=8F=E5=88=97=E5=8C=96=E7=9A=84=20sdv=20=E6=B5=8B=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: 王柄涵 --- src/deserializer.rs | 153 +++++++++--- src/error.rs | 24 ++ src/lib.rs | 2 +- src/value/index.rs | 2 +- tests/sdv_adapt_serde_test.rs | 442 ++++++++++++++++++++++++++++++++++ 5 files changed, 585 insertions(+), 38 deletions(-) create mode 100644 tests/sdv_adapt_serde_test.rs diff --git a/src/deserializer.rs b/src/deserializer.rs index 864d828..27b54d4 100644 --- a/src/deserializer.rs +++ b/src/deserializer.rs @@ -108,8 +108,8 @@ where { let mut deserializer = Deserializer::new_from_io(reader); let t = T::deserialize(&mut deserializer)?; - match deserializer.reader.peek() { - Ok(None) => Ok(t), + match eat_whitespace_until_not!(deserializer) { + None => Ok(t), _ => Err(Error::Parsing(ParsingUnfinished)), } } @@ -141,9 +141,11 @@ where { let mut deserializer = Deserializer::new_from_slice(slice); let t = T::deserialize(&mut deserializer)?; - match deserializer.reader.peek() { - Ok(None) => Ok(t), - _ => Err(Error::Parsing(ParsingUnfinished)), + match eat_whitespace_until_not!(deserializer) { + None => Ok(t), + _ => { + unexpected_character!(&mut deserializer) + } } } @@ -415,7 +417,6 @@ where let peek_ch = match eat_whitespace_until_not!(self) { Some(ch) => ch, None => { - println!("arrive optin None branch"); return Err(Error::Parsing(ParsingUnfinished)); } }; @@ -601,7 +602,9 @@ where } _ => { if self.next_char()? == Some(b'{') { + eat_whitespace_until_not!(self); let value = visitor.visit_enum(EnumAssistant::new(self))?; + eat_whitespace_until_not!(self); if self.next_char()? == Some(b'}') { Ok(value) @@ -661,25 +664,36 @@ where T: DeserializeSeed<'de>, { let peek_ch = match eat_whitespace_until_not!(self.deserializer) { - Some(ch) => ch, - None => { - return Err(Error::Parsing(ParsingUnfinished)); + Some(b']') => return Ok(None), + Some(b',') if !self.is_first => { + self.deserializer.discard_char(); + eat_whitespace_until_not!(self.deserializer) } + Some(ch) => { + if self.is_first { + self.is_first = false; + Some(ch) + } else { + let position = self.deserializer.reader.position(); + return Err(Error::Parsing(MissingComma( + position.line(), + position.column(), + ))); + } + } + None => return Err(Error::Parsing(ParsingUnfinished)), }; match peek_ch { - b']' => Ok(None), - b',' if self.is_first => { - unexpected_character!(self.deserializer) - } - b',' => { - self.deserializer.discard_char(); - Ok(Some(seed.deserialize(&mut *self.deserializer)?)) - } - _ => { - self.is_first = false; - Ok(Some(seed.deserialize(&mut *self.deserializer)?)) + Some(b']') => { + let position = self.deserializer.reader.position(); + Err(Error::Parsing(TrailingComma( + position.line(), + position.column(), + ))) } + Some(_) => Ok(Some(seed.deserialize(&mut *self.deserializer)?)), + None => Err(Error::Parsing(ParsingUnfinished)), } } } @@ -689,30 +703,45 @@ where R: BytesReader + Cacheable, { type Error = Error; + fn next_key_seed(&mut self, seed: K) -> Result, Error> where K: DeserializeSeed<'de>, { let peek_ch = match eat_whitespace_until_not!(self.deserializer) { - Some(ch) => ch, + Some(b'}') => return Ok(None), + Some(b',') if !self.is_first => { + self.deserializer.discard_char(); + eat_whitespace_until_not!(self.deserializer) + } + Some(ch) => { + if self.is_first { + self.is_first = false; + Some(ch) + } else { + let position = self.deserializer.reader.position(); + return Err(Error::Parsing(MissingComma( + position.line(), + position.column(), + ))); + } + } None => { return Err(Error::Parsing(ParsingUnfinished)); } }; match peek_ch { - b'}' => Ok(None), - b',' if self.is_first => { - unexpected_character!(self.deserializer) - } - b',' => { - self.deserializer.discard_char(); - Ok(Some(seed.deserialize(&mut *self.deserializer)?)) - } - _ => { - self.is_first = false; - Ok(Some(seed.deserialize(&mut *self.deserializer)?)) + Some(b'"') => Ok(Some(seed.deserialize(&mut *self.deserializer)?)), + Some(b'}') => { + let position = self.deserializer.reader.position(); + Err(Error::Parsing(TrailingComma( + position.line(), + position.column(), + ))) } + // Object key must be String. + _ => unexpected_character!(self.deserializer), } } @@ -723,9 +752,16 @@ where match eat_whitespace_until_not!(self.deserializer) { Some(b':') => { self.deserializer.discard_char(); + eat_whitespace_until_not!(self.deserializer); seed.deserialize(&mut *self.deserializer) } - Some(_ch) => unexpected_character!(self.deserializer), + Some(_ch) => { + let position = self.deserializer.reader.position(); + Err(Error::Parsing(MissingColon( + position.line(), + position.column(), + ))) + } None => Err(Error::Parsing(ParsingUnfinished)), } } @@ -884,12 +920,24 @@ mod ut_test_for_deserializer { let expected: Vec = vec![1, 2, 3]; assert_eq!(expected, from_slice::>(slice_seq).unwrap()); - let slice_map = r#"{ true : 1 }"#.as_bytes(); + let slice_map = r#"{ "apple" : 3 }"#.as_bytes(); let mut expected = HashMap::new(); - expected.insert(true, 1); + expected.insert("appple", 3); assert_eq!( expected, - from_slice::>(slice_map).unwrap() + crate::from_slice::>(slice_map).unwrap() + ); + } + + #[test] + fn de_map() { + use std::collections::HashMap; + let slice_map = r#"{ "apple" : 3 }"#.as_bytes(); + let mut expected = HashMap::new(); + expected.insert(String::from("apple"), 3); + assert_eq!( + expected, + from_slice::>(slice_map).unwrap() ); } @@ -1024,10 +1072,43 @@ mod ut_test_for_deserializer { }; assert_eq!(expected, from_str(str).unwrap()); + let str = r#"{ + "int" : 1 , + "seq" : ["abcd" , "efgh" ], + "tup" : [1, 2 ,3 ] + }"#; + let expected = Test { + int: 1, + seq: vec![String::from("abcd"), String::from("efgh")], + tup: (1, 2, 3), + }; + assert_eq!(expected, from_str(str).unwrap()); + // The following is the test for abnormal input of JSON. + // Missing '}' at the end. let str_abnormal = r#"{"int":1,"seq":["abcd","efgh"],"tup":[1,2,3]"#; let res = from_str::(str_abnormal); assert!(res.is_err()); + + // Missing ','. + let str_abnormal = r#"{"int":1 "seq":["abcd","efgh"],"tup":[1,2,3]"#; + let res = from_str::(str_abnormal); + assert!(res.is_err()); + + // Trailing ','. + let str_abnormal = r#"{"int":1, "seq":["abcd","efgh",],"tup":[1,2,3]"#; + let res = from_str::(str_abnormal); + assert!(res.is_err()); + + // Missing ':'. + let str_abnormal = r#"{"int":1, "seq":["abcd","efgh"],"tup" [1,2,3]"#; + let res = from_str::(str_abnormal); + assert!(res.is_err()); + + // Incorrect field name. + let str_abnormal = r#"{"it":1, "sq" : ["abcd","efgh"],"tp": [1,2,3]"#; + let res = from_str::(str_abnormal); + assert!(res.is_err()); } /// UT test to deserialize enum diff --git a/src/error.rs b/src/error.rs index 29313e8..e85a5fd 100644 --- a/src/error.rs +++ b/src/error.rs @@ -61,6 +61,15 @@ pub enum ParseError { /// The input sequence has not yet been parsed. ParsingUnfinished, + + /// There is an extra comma after the last value in an array or map (line number, character number) + TrailingComma(usize, usize), + + /// A colon is missing (line number, character number) + MissingColon(usize, usize), + + /// A comma is missing (line number, character number) + MissingComma(usize, usize), } impl Error { @@ -101,6 +110,21 @@ impl Debug for ParseError { Self::ParsingUnfinished => { write!(f, "[Error]: Value has not been fully deserialized.") } + Self::TrailingComma(line, pos) => { + write!( + f, + "[Line]: {line}, [Pos]: {pos}, [Error]: Has a comma after the last value in an array or map." + ) + } + Self::MissingColon(line, pos) => { + write!(f, "[Line]: {line}, [Pos]: {pos}, [Error]: A colon is missing between key and value.") + } + Self::MissingComma(line, pos) => { + write!( + f, + "[Line]: {line}, [Pos]: {pos}, [Error]: A comma is missing before next value." + ) + } } } } diff --git a/src/lib.rs b/src/lib.rs index fc31719..84c8fd9 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -60,7 +60,6 @@ mod states; mod value; pub use error::{Error, ParseError}; -pub use serializer_compact::to_string; pub use value::{Array, Index, JsonValue, Number, Object}; pub(crate) use encoder::{CompactEncoder, FormattedEncoder}; @@ -82,3 +81,4 @@ pub(crate) use linked_list::{Cursor, CursorMut, LinkedList}; pub use linked_list::{Iter, IterMut, Node}; pub use deserializer::{from_reader, from_slice, from_str}; +pub use serializer_compact::{to_string, to_writer}; diff --git a/src/value/index.rs b/src/value/index.rs index 56d0d43..bd4163a 100644 --- a/src/value/index.rs +++ b/src/value/index.rs @@ -215,7 +215,7 @@ impl Index for str { /// ``` fn index_into_mut<'a>(&self, value: &'a mut JsonValue) -> &'a mut JsonValue { if let JsonValue::Object(ref mut object) = value { - #[cfg(any(feature = "list_object"))] + #[cfg(feature = "list_object")] { return object.get_key_mut_maybe_insert(self); } diff --git a/tests/sdv_adapt_serde_test.rs b/tests/sdv_adapt_serde_test.rs new file mode 100644 index 0000000..c8e1b96 --- /dev/null +++ b/tests/sdv_adapt_serde_test.rs @@ -0,0 +1,442 @@ +// Copyright (c) 2023 Huawei Device Co., Ltd. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use serde::Deserialize; +use std::borrow::Cow; +use ylong_json::from_str; + +#[derive(Deserialize, PartialEq, Debug)] +struct ExampleOne { + #[serde(rename(deserialize = "Image"))] + image: InnerImage, +} + +#[derive(Deserialize, PartialEq, Debug)] +struct InnerImage { + #[serde(rename(deserialize = "Width"))] + width: u32, + #[serde(rename(deserialize = "Height"))] + height: u32, + #[serde(rename(deserialize = "Title"))] + title: String, + #[serde(rename(deserialize = "Thumbnail"))] + thumbnail: InnerThumbnail, + #[serde(rename(deserialize = "Animated"))] + animated: bool, + #[serde(rename(deserialize = "IDs"))] + ids: Vec, +} + +#[derive(Deserialize, PartialEq, Debug)] +struct InnerThumbnail { + #[serde(rename(deserialize = "Url"))] + url: String, + #[serde(rename(deserialize = "Height"))] + height: u32, + #[serde(rename(deserialize = "Width"))] + width: u32, +} + +const RFC7159_EXAMPLE1: &str = r#" +{ + "Image": { + "Width": 800, + "Height": 600, + "Title": "View from 15th Floor", + "Thumbnail": { + "Url": "http://www.example.com/image/481989943", + "Height": 125, + "Width": 100 + }, + "Animated" : false, + "IDs": [116, 943, 234, 38793] + } +} +"#; + +#[test] +fn sdv_adapt_serde_example_one() { + let de_res = from_str::(RFC7159_EXAMPLE1); + let expected = ExampleOne { + image: InnerImage { + width: 800, + height: 600, + thumbnail: InnerThumbnail { + url: String::from("http://www.example.com/image/481989943"), + height: 125, + width: 100, + }, + animated: false, + ids: vec![116, 943, 234, 38793], + title: String::from("View from 15th Floor"), + }, + }; + assert_eq!(expected, de_res.unwrap()); +} + +const RFC7159_EXAMPLE2: &str = r#" +[ + { + "precision": "zip", + "Latitude": 37.7668, + "Longitude": -122.3959, + "Address": "", + "City": "SAN FRANCISCO", + "State": "CA", + "Zip": "94107", + "Country": "US" + }, + { + "precision": "zip", + "Latitude": 37.371991, + "Longitude": -122.026020, + "Address": "", + "City": "SUNNYVALE", + "State": "CA", + "Zip": "94085", + "Country": "US" + } +] +"#; + +#[derive(Deserialize, PartialEq, Debug)] +struct ExampleTwo<'a> { + precision: String, + #[serde(rename(deserialize = "Latitude"))] + latitude: f32, + #[serde(rename(deserialize = "Longitude"))] + longitude: f32, + #[serde(rename(deserialize = "Address"))] + address: String, + #[serde(rename(deserialize = "City"))] + city: String, + #[serde(rename(deserialize = "State"))] + state: String, + #[serde(rename(deserialize = "Zip"))] + zip: Cow<'a, str>, + #[serde(rename(deserialize = "Country"))] + country: Cow<'a, str>, +} + +#[test] +fn sdv_adapt_serde_example_two() { + let de_res = from_str::>(RFC7159_EXAMPLE2).unwrap(); + let expected_0 = ExampleTwo { + precision: String::from("zip"), + latitude: 37.7668, + longitude: -122.3959, + address: String::from(""), + city: String::from("SAN FRANCISCO"), + state: String::from("CA"), + zip: Cow::from("94107"), + country: Cow::from("US"), + }; + let expected_1 = ExampleTwo { + precision: String::from("zip"), + latitude: 37.371_991, + longitude: -122.026_02, + address: String::from(""), + city: String::from("SUNNYVALE"), + state: String::from("CA"), + zip: Cow::from("94085"), + country: Cow::from("US"), + }; + assert_eq!(expected_0, de_res[0]); + assert_eq!(expected_1, de_res[1]); +} + +const JSON_PARSE_EXAMPLE3: &str = r#" +{ + "null_test" : { + "null1": null + }, + "bool_test" : { + "boolean1": true, + "boolean2": false + }, + "number_test" : { + "number1": 0, + "number2": -0, + "number3": 123, + "number4": -123, + "number5": 123.456, + "number6": -123.456, + "number7": 123.456e+7, + "number8": 123.456e-7, + "number9": 123.456E+7, + "number10": 123.456E-7, + "number11": -123.456e+7, + "number12": -123.456e-7, + "number13": -123.456E+7, + "number14": -123.456E-7, + "number15": 0.0, + "number16": -0.0e+7, + "number17": 3e2 + }, + "string_test" : { + "string1": "", + "string2": "Hello World", + "string3": "abcdefghijklmnopqrstuvwxyz", + "string4": "ABCDEFGHIJKLMNOPQRSTUVWXYZ", + "string5": "0123456789", + "string6": " \b\f\n\r\t", + "string7": "\"\\\/", + "string8": "`1~!@#$%^&*()_+-={':[,]}|;.?", + "string9": "\u0123\u4567\u89AB\uCDEF\uabcd\uef4A" + }, + "array_test" : { + "array1": [ ], + "array2": [ true, false + ], + "array3": [0 , 1 , -1 , 100 , -100], + "array4": [ + 0.0 , -0.0, 1.0 , + -1.0, 12.34 + , -12.34 ], + "array5": [[[[[[["nest"]]]]]]] + }, + "object_test" : { + "object1": null, + "object2": null , + "object3": {"key1":"Null","key2": {"Bool":true}, + "key3":{"Number":0.0},"key4":{"StringVal":"string"}, + "key5":{"Array":[]},"key6":{"Object":null}}, + "object4": { + "key1" : "Null" , "key2" + : { "Bool" : true } , "key3" : + { "Number" : 0.0 } , "key4":{"StringVal":"string"} , + "key5": {"Array":[]}, "key6": { + "Object":null } + }, + "object5": {"nest1": {"nest2": {"nest3": {"nest4": null}}}}, + "": { + "": "key1", + "\/\\\"\uCAFE\uBABE\uAB98\uFCDE\ubcda\uef4A\b\f\n\r\t`1~!@#$%^&*()_+-=[]{}|;:',./<>?" : "key2" + } + } +} +"#; + +#[derive(Deserialize, PartialEq, Debug)] +struct ParseTest { + null_test: NullTest, + bool_test: BoolTest, + number_test: NumberTest, + string_test: StringTest, + array_test: ArrayTest, + object_test: ObjectTest, +} + +#[derive(Deserialize, PartialEq, Debug)] +struct StructNull; + +#[derive(Deserialize, PartialEq, Debug)] +struct NullTest { + null1: StructNull, +} + +#[derive(Deserialize, PartialEq, Debug)] +struct BoolTest { + boolean1: bool, + boolean2: bool, +} + +#[derive(Deserialize, PartialEq, Debug)] +struct NumberTest { + number1: u8, + number2: i8, + number3: u16, + number4: i16, + number5: f32, + number6: f32, + number7: f64, + number8: f64, + number9: f64, + number10: f64, + number11: f64, + number12: f64, + number13: f64, + number14: f64, + number15: f64, + number16: f64, + number17: f64, +} + +#[derive(Deserialize, PartialEq, Debug)] +struct StringTest { + string1: String, + string2: String, + string3: String, + string4: String, + string5: String, + string6: String, + string7: String, + string8: String, + string9: String, +} + +#[derive(Deserialize, PartialEq, Debug)] +struct ArrayTest { + array1: Vec, + array2: Vec, + array3: Vec, + array4: Vec, + #[warn(clippy::type_complexity)] + array5: Vec>>>>>>, +} + +#[derive(Deserialize, PartialEq, Debug)] +struct ObjectTest { + object1: StructNull, + object2: StructNull, + object3: ObjectThreeFour, + object4: ObjectThreeFour, + object5: ObjectFive, + #[serde(rename(deserialize = ""))] + object6: ObjectSix, +} + +#[derive(Deserialize, PartialEq, Debug)] +enum InnerValue { + Null, + Bool(bool), + Number(f64), + StringVal(String), + Array(Vec), + Object(StructNull), +} + +#[derive(Deserialize, PartialEq, Debug)] +struct ObjectThreeFour { + key1: InnerValue, + key2: InnerValue, + key3: InnerValue, + key4: InnerValue, + key5: InnerValue, + key6: InnerValue, +} + +#[derive(Deserialize, PartialEq, Debug)] +struct ObjectFive { + nest1: NestOne, +} + +#[derive(Deserialize, PartialEq, Debug)] +struct NestOne { + nest2: NestTwo, +} + +#[derive(Deserialize, PartialEq, Debug)] +struct NestTwo { + nest3: NestThree, +} + +#[derive(Deserialize, PartialEq, Debug)] +struct NestThree { + nest4: NestFour, +} + +#[derive(Deserialize, PartialEq, Debug)] +struct NestFour; + +#[derive(Deserialize, PartialEq, Debug)] +struct ObjectSix { + #[serde(rename(deserialize = ""))] + mem1: String, + #[serde(rename( + deserialize = "/\\\"\u{CAFE}\u{BABE}\u{AB98}\u{FCDE}\u{bcda}\u{ef4A}\u{0008}\u{000c}\n\r\t`1~!@#$%^&*()_+-=[]{}|;:',./<>?" + ))] + mem2: String, +} + +#[test] +fn sdv_adapt_serde_example_three() { + let de_res = from_str::(JSON_PARSE_EXAMPLE3); + let expected = ParseTest { + null_test: NullTest { null1: StructNull }, + bool_test: BoolTest { + boolean1: true, + boolean2: false, + }, + number_test: NumberTest { + number1: 0, + number2: 0, + number3: 123, + number4: -123, + number5: 123.456, + number6: -123.456, + number7: 1234560000.0, + number8: 1.23456e-5, + number9: 1234560000.0, + number10: 1.23456e-5, + number11: -1234560000.0, + number12: -1.23456e-5, + number13: -1234560000.0, + number14: -1.23456e-5, + number15: 0.0, + number16: -0.0, + number17: 300.0, + }, + string_test: StringTest { + string1: String::from(""), + string2: String::from("Hello World"), + string3: String::from("abcdefghijklmnopqrstuvwxyz"), + string4: String::from("ABCDEFGHIJKLMNOPQRSTUVWXYZ"), + + string5: String::from("0123456789"), + string6: String::from(" \u{8}\u{c}\n\r\t"), + string7: String::from("\"\\/"), + string8: String::from("`1~!@#$%^&*()_+-={':[,]}|;.?"), + string9: String::from("ģ䕧覫췯ꯍ\u{ef4a}"), + }, + array_test: ArrayTest { + array1: vec![], + array2: vec![true, false], + array3: vec![0, 1, -1, 100, -100], + array4: vec![0.0, -0.0, 1.0, -1.0, 12.34, -12.34], + array5: vec![vec![vec![vec![vec![vec![vec![String::from("nest")]]]]]]], + }, + object_test: ObjectTest { + object1: StructNull, + object2: StructNull, + object3: ObjectThreeFour { + key1: InnerValue::Null, + key2: InnerValue::Bool(true), + key3: InnerValue::Number(0.0), + key4: InnerValue::StringVal(String::from("string")), + key5: InnerValue::Array(vec![]), + key6: InnerValue::Object(StructNull), + }, + object4: ObjectThreeFour { + key1: InnerValue::Null, + key2: InnerValue::Bool(true), + key3: InnerValue::Number(0.0), + key4: InnerValue::StringVal(String::from("string")), + key5: InnerValue::Array(vec![]), + key6: InnerValue::Object(StructNull), + }, + object5: ObjectFive { + nest1: NestOne { + nest2: NestTwo { + nest3: NestThree { nest4: NestFour }, + }, + }, + }, + object6: ObjectSix { + mem1: String::from("key1"), + mem2: String::from("key2"), + }, + }, + }; + assert_eq!(expected, de_res.unwrap()); +}