From db9fc0abd98f4b153373ef2ca3709c84a0faad94 Mon Sep 17 00:00:00 2001 From: Benedikt Schneppe Date: Mon, 30 Dec 2024 10:45:43 +0100 Subject: [PATCH 1/6] fix(deserialization): nested deserialization (#200) changes condition of start element next_event call for structs, always break for EndDocument --- yaserde/tests/deserializer.rs | 77 ++++++++++++++++++++++++++ yaserde_derive/src/de/expand_struct.rs | 7 +-- 2 files changed, 80 insertions(+), 4 deletions(-) diff --git a/yaserde/tests/deserializer.rs b/yaserde/tests/deserializer.rs index 5b0e3af..c1f03c8 100644 --- a/yaserde/tests/deserializer.rs +++ b/yaserde/tests/deserializer.rs @@ -1117,3 +1117,80 @@ fn de_nested_macro_rules() { float_attrs!(f32); } + + +#[test] +fn de_nested_element_equality() { + + + #[derive(YaDeserialize, Debug, PartialEq)] + #[yaserde(rename = "EBMLSchema")] + struct Schema { + #[yaserde(rename = "element")] + elements: Vec, + } + + #[derive(YaDeserialize, Debug, PartialEq)] + #[yaserde(rename = "element")] + struct Element { + #[yaserde(rename = "documentation")] + documentation: Vec, + } + + #[derive(YaDeserialize, Debug, PartialEq)] + #[yaserde(rename = "documentation")] + struct Documentation { + #[yaserde(text=true)] + body: String, + } + + let parent = r#" + +Test + +"#; + let parent: Schema = yaserde::de::from_str(parent).unwrap(); + + let child = r#" +Test +"#; + let child: Element = yaserde::de::from_str(child).unwrap(); + + assert_ne!(parent.elements, vec![]); + assert_eq!(parent.elements[0], child); +} + +#[test] +fn de_nested_3_levels(){ + + #[derive(YaSerialize,YaDeserialize,Debug,PartialEq)] + struct A { + id: String, + #[yaserde(rename = "SAME")] + many:Vec + } + + #[derive(YaSerialize,YaDeserialize,Debug,PartialEq)] + struct B { + id: Option, + #[yaserde(rename = "SAME")] + many:Vec + + } + + #[derive(YaSerialize,YaDeserialize,Debug,PartialEq)] + struct C { + id: Option, + } + + let content = r#"a1b1c1c2"#; + let model = A { + id: "a1".to_string(), + many: vec![B { id: Some("b1".to_string()), many: vec![ + C { id: Some("c1".to_string() )}, + C { id: Some("c2".to_string() )}, + ] }], + }; + deserialize_and_validate!(content, model, A); + +} diff --git a/yaserde_derive/src/de/expand_struct.rs b/yaserde_derive/src/de/expand_struct.rs index fca7486..9c1b30e 100644 --- a/yaserde_derive/src/de/expand_struct.rs +++ b/yaserde_derive/src/de/expand_struct.rs @@ -421,7 +421,7 @@ pub fn parse( match event { ::yaserde::__xml::reader::XmlEvent::StartElement{ref name, ref attributes, ..} => { let namespace = name.namespace.clone().unwrap_or_default(); - if depth == 0 && name.local_name == #root && namespace.as_str() == #root_namespace { + if depth == 0 { // Consume root element. We must do this first. In the case it shares a name with a child element, we don't // want to prematurely match the child element below. let event = reader.next_event()?; @@ -457,9 +457,8 @@ pub fn parse( depth -= 1; } ::yaserde::__xml::reader::XmlEvent::EndDocument => { - if #flatten { - break; - } + // once we receive this once, we will keep getting it, potentially looping forever + break; } ::yaserde::__xml::reader::XmlEvent::Characters(ref text_content) => { #set_text From 358e0b7c3d9d87297f411d6c2d48ee24a7288e35 Mon Sep 17 00:00:00 2001 From: Benedikt Schneppe Date: Mon, 30 Dec 2024 17:34:06 +0100 Subject: [PATCH 2/6] style: fix formatting --- yaserde/tests/deserializer.rs | 44 ++++++++++++++++++----------------- 1 file changed, 23 insertions(+), 21 deletions(-) diff --git a/yaserde/tests/deserializer.rs b/yaserde/tests/deserializer.rs index c1f03c8..dc15760 100644 --- a/yaserde/tests/deserializer.rs +++ b/yaserde/tests/deserializer.rs @@ -1118,11 +1118,8 @@ fn de_nested_macro_rules() { float_attrs!(f32); } - #[test] fn de_nested_element_equality() { - - #[derive(YaDeserialize, Debug, PartialEq)] #[yaserde(rename = "EBMLSchema")] struct Schema { @@ -1140,10 +1137,10 @@ fn de_nested_element_equality() { #[derive(YaDeserialize, Debug, PartialEq)] #[yaserde(rename = "documentation")] struct Documentation { - #[yaserde(text=true)] + #[yaserde(text = true)] body: String, } - + let parent = r#" Test @@ -1155,42 +1152,47 @@ fn de_nested_element_equality() { Test "#; let child: Element = yaserde::de::from_str(child).unwrap(); - + assert_ne!(parent.elements, vec![]); assert_eq!(parent.elements[0], child); } #[test] -fn de_nested_3_levels(){ - - #[derive(YaSerialize,YaDeserialize,Debug,PartialEq)] +fn de_nested_3_levels() { + #[derive(YaSerialize, YaDeserialize, Debug, PartialEq)] struct A { id: String, #[yaserde(rename = "SAME")] - many:Vec + many: Vec, } - #[derive(YaSerialize,YaDeserialize,Debug,PartialEq)] + #[derive(YaSerialize, YaDeserialize, Debug, PartialEq)] struct B { id: Option, #[yaserde(rename = "SAME")] - many:Vec - + many: Vec, } - #[derive(YaSerialize,YaDeserialize,Debug,PartialEq)] + #[derive(YaSerialize, YaDeserialize, Debug, PartialEq)] struct C { id: Option, } - let content = r#"a1b1c1c2"#; - let model = A { + let content = + r#"a1b1c1c2"#; + let model = A { id: "a1".to_string(), - many: vec![B { id: Some("b1".to_string()), many: vec![ - C { id: Some("c1".to_string() )}, - C { id: Some("c2".to_string() )}, - ] }], + many: vec![B { + id: Some("b1".to_string()), + many: vec![ + C { + id: Some("c1".to_string()), + }, + C { + id: Some("c2".to_string()), + }, + ], + }], }; deserialize_and_validate!(content, model, A); - } From 1b281bc7e5094853226e9be233b2aec2ac7759e5 Mon Sep 17 00:00:00 2001 From: Benedikt Schneppe Date: Mon, 30 Dec 2024 19:38:43 +0100 Subject: [PATCH 3/6] fix: keep namespace + name checking but go against root label if available --- yaserde/src/de/mod.rs | 2 + yaserde/tests/deserializer.rs | 51 ++++++++++++++++++++++++++ yaserde_derive/src/de/expand_struct.rs | 5 ++- 3 files changed, 56 insertions(+), 2 deletions(-) diff --git a/yaserde/src/de/mod.rs b/yaserde/src/de/mod.rs index b08b138..5840820 100644 --- a/yaserde/src/de/mod.rs +++ b/yaserde/src/de/mod.rs @@ -18,6 +18,7 @@ pub struct Deserializer { depth: usize, reader: EventReader, peeked: Option, + pub inner_struct_label: Option<&'static str>, } impl Deserializer { @@ -26,6 +27,7 @@ impl Deserializer { depth: 0, reader, peeked: None, + inner_struct_label: None, } } diff --git a/yaserde/tests/deserializer.rs b/yaserde/tests/deserializer.rs index dc15760..4466d65 100644 --- a/yaserde/tests/deserializer.rs +++ b/yaserde/tests/deserializer.rs @@ -1196,3 +1196,54 @@ fn de_nested_3_levels() { }; deserialize_and_validate!(content, model, A); } + +#[test] +fn de_nested_issue_192() { + #[derive(Clone, Default, Debug, PartialEq, YaDeserialize)] + #[yaserde( + prefix = "xs", + namespaces = { + "xs" = "http://www.w3.org/2001/XMLSchema", + } + )] + pub struct XSDGroup { + #[yaserde(rename = "ref", attribute = true)] + pub reference: String, + } + + #[derive(Clone, Default, Debug, PartialEq, YaDeserialize)] + #[yaserde( + rename = "sequence", + prefix = "xs", + namespaces = { + "xs" = "http://www.w3.org/2001/XMLSchema", + } + )] + pub struct Sequence { + #[yaserde(rename = "group", prefix = "xs")] + pub groups: Vec, + + #[yaserde(rename = "sequence", prefix = "xs")] + pub sequences: Vec, + } + + let content = r#" + + + + + "#; + let model = Sequence { + groups: vec![ + XSDGroup { + reference: "AR:AR-OBJECT".to_string(), + }, + XSDGroup { + reference: "AR:AUTOSAR".to_string(), + }, + ], + sequences: vec![], + }; + + deserialize_and_validate!(content, model, Sequence); +} diff --git a/yaserde_derive/src/de/expand_struct.rs b/yaserde_derive/src/de/expand_struct.rs index 9c1b30e..1fdc7a5 100644 --- a/yaserde_derive/src/de/expand_struct.rs +++ b/yaserde_derive/src/de/expand_struct.rs @@ -160,6 +160,7 @@ pub fn parse( } if let Ok(::yaserde::__xml::reader::XmlEvent::StartElement { .. }) = reader.peek() { // If substruct's start element found then deserialize substruct + reader.inner_struct_label = Some(#label_name); let value = <#struct_name as ::yaserde::YaDeserialize>::deserialize(reader)?; #value_label #action; // read EndElement @@ -383,7 +384,6 @@ pub fn parse( build_code_for_unused_xml_events(&call_flatten_visitors) }; - let flatten = root_attributes.flatten; let (impl_generics, ty_generics, where_clause) = generics.split_for_impl(); quote! { @@ -392,6 +392,7 @@ pub fn parse( fn deserialize( reader: &mut ::yaserde::de::Deserializer, ) -> ::std::result::Result { + let root_label= reader.inner_struct_label.take().unwrap_or(#root); let (named_element, struct_namespace) = if let ::yaserde::__xml::reader::XmlEvent::StartElement { name, .. } = reader.peek()?.to_owned() { (name.local_name.to_owned(), name.namespace.clone()) @@ -421,7 +422,7 @@ pub fn parse( match event { ::yaserde::__xml::reader::XmlEvent::StartElement{ref name, ref attributes, ..} => { let namespace = name.namespace.clone().unwrap_or_default(); - if depth == 0 { + if depth == 0 && name.local_name == root_label && namespace.as_str() == #root_namespace { // Consume root element. We must do this first. In the case it shares a name with a child element, we don't // want to prematurely match the child element below. let event = reader.next_event()?; From 70e65434c190538b3072b95492d979452d7acef9 Mon Sep 17 00:00:00 2001 From: Benedikt Schneppe Date: Tue, 31 Dec 2024 00:04:19 +0100 Subject: [PATCH 4/6] fix: drop namespace equality check --- yaserde_derive/src/de/expand_struct.rs | 3 +-- yaserde_derive/src/de/mod.rs | 12 ------------ 2 files changed, 1 insertion(+), 14 deletions(-) diff --git a/yaserde_derive/src/de/expand_struct.rs b/yaserde_derive/src/de/expand_struct.rs index 1fdc7a5..7a6a81b 100644 --- a/yaserde_derive/src/de/expand_struct.rs +++ b/yaserde_derive/src/de/expand_struct.rs @@ -7,7 +7,6 @@ use syn::{DataStruct, Generics, Ident}; pub fn parse( data_struct: &DataStruct, name: &Ident, - root_namespace: &str, root: &str, root_attributes: &YaSerdeAttribute, generics: &Generics, @@ -422,7 +421,7 @@ pub fn parse( match event { ::yaserde::__xml::reader::XmlEvent::StartElement{ref name, ref attributes, ..} => { let namespace = name.namespace.clone().unwrap_or_default(); - if depth == 0 && name.local_name == root_label && namespace.as_str() == #root_namespace { + if depth == 0 && name.local_name == root_label { // Consume root element. We must do this first. In the case it shares a name with a child element, we don't // want to prematurely match the child element below. let event = reader.next_event()?; diff --git a/yaserde_derive/src/de/mod.rs b/yaserde_derive/src/de/mod.rs index 4526209..2ed9f9c 100644 --- a/yaserde_derive/src/de/mod.rs +++ b/yaserde_derive/src/de/mod.rs @@ -15,23 +15,11 @@ pub fn expand_derive_deserialize(ast: &syn::DeriveInput) -> Result expand_struct::parse( data_struct, name, - &root_namespace, &root_name, &root_attributes, generics, From f1a52f218a1b7e93d8c53386e67d42b46548fa00 Mon Sep 17 00:00:00 2001 From: Benedikt Schneppe Date: Thu, 2 Jan 2025 09:50:44 +0100 Subject: [PATCH 5/6] style: formatting --- yaserde_derive/src/de/mod.rs | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/yaserde_derive/src/de/mod.rs b/yaserde_derive/src/de/mod.rs index 2ed9f9c..99d243a 100644 --- a/yaserde_derive/src/de/mod.rs +++ b/yaserde_derive/src/de/mod.rs @@ -17,13 +17,9 @@ pub fn expand_derive_deserialize(ast: &syn::DeriveInput) -> Result expand_struct::parse( - data_struct, - name, - &root_name, - &root_attributes, - generics, - ), + syn::Data::Struct(ref data_struct) => { + expand_struct::parse(data_struct, name, &root_name, &root_attributes, generics) + } syn::Data::Enum(ref data_enum) => { expand_enum::parse(data_enum, name, &root_name, &root_attributes, generics) } From df94be609b31ef34e65c6c297740e511dceeaa0b Mon Sep 17 00:00:00 2001 From: Benedikt Schneppe Date: Fri, 17 Jan 2025 18:34:21 +0100 Subject: [PATCH 6/6] test: fix test input after xml-rs change see: https://github.com/kornelski/xml-rs/commit/4a5ba62fb30872a2bb0648150de2c195519bedcc --- examples/tests/data/svd.xml | 2 +- yaserde/src/lib.rs | 2 +- yaserde/tests/cdata.rs | 4 ++-- yaserde/tests/deserializer.rs | 30 +++++++++++++++--------------- yaserde/tests/option.rs | 2 +- 5 files changed, 20 insertions(+), 20 deletions(-) diff --git a/examples/tests/data/svd.xml b/examples/tests/data/svd.xml index 8743645..0d806ae 100644 --- a/examples/tests/data/svd.xml +++ b/examples/tests/data/svd.xml @@ -1,4 +1,4 @@ - + Renesas diff --git a/yaserde/src/lib.rs b/yaserde/src/lib.rs index 6c86ac9..669e30e 100644 --- a/yaserde/src/lib.rs +++ b/yaserde/src/lib.rs @@ -328,7 +328,7 @@ macro_rules! serialize_and_validate { log::debug!("serialize_and_validate @ {}:{}", file!(), line!()); let data: Result = yaserde::ser::to_string(&$model); - let content = &format!(r#"{}"#, $content); + let content = &format!(r#"{}"#, $content); assert_eq!( data, Ok(content.split("\n").map(|s| s.trim()).collect::()) diff --git a/yaserde/tests/cdata.rs b/yaserde/tests/cdata.rs index a73868d..45b980f 100644 --- a/yaserde/tests/cdata.rs +++ b/yaserde/tests/cdata.rs @@ -18,14 +18,14 @@ fn test_cdata_serialization() { msgdata: "Some unescaped content".to_string(), }; let xml_output = yaserde::ser::to_string(&test_data).expect("Serialization failed"); - let expected_output = r#"Some unescaped content]]>"#; + let expected_output = r#"Some unescaped content]]>"#; assert_eq!(xml_output, expected_output); } #[test] fn test_cdata_deserialization() { init(); - let xml = r#"Some unescaped content]]>"#; + let xml = r#"Some unescaped content]]>"#; let r: TestStruct = yaserde::de::from_str(xml).unwrap(); let expected_output = TestStruct { msgdata: "Some unescaped content".to_string(), diff --git a/yaserde/tests/deserializer.rs b/yaserde/tests/deserializer.rs index 4466d65..52112d7 100644 --- a/yaserde/tests/deserializer.rs +++ b/yaserde/tests/deserializer.rs @@ -583,7 +583,7 @@ fn de_complex_enum() { Dotted(u32), } - let content = r#" + let content = r#" text @@ -598,7 +598,7 @@ fn de_complex_enum() { } ); - let content = r#" + let content = r#" text @@ -613,7 +613,7 @@ fn de_complex_enum() { } ); - let content = r#" + let content = r#" 56 @@ -628,7 +628,7 @@ fn de_complex_enum() { } ); - let content = r#" + let content = r#" @@ -646,7 +646,7 @@ fn de_complex_enum() { } ); - let content = r#" + let content = r#" text @@ -661,7 +661,7 @@ fn de_complex_enum() { } ); - let content = r#" + let content = r#" @@ -679,7 +679,7 @@ fn de_complex_enum() { } ); - let content = r#" + let content = r#" abc @@ -695,7 +695,7 @@ fn de_complex_enum() { } ); - let content = r#" + let content = r#" 12 @@ -711,7 +711,7 @@ fn de_complex_enum() { } ); - let content = r#" + let content = r#" 1223 @@ -730,7 +730,7 @@ fn de_complex_enum() { } ); - let content = r#" + let content = r#" 1223 @@ -749,7 +749,7 @@ fn de_complex_enum() { } ); - let content = r#" + let content = r#" 54 @@ -854,7 +854,7 @@ fn de_subitem_issue_12() { } convert_and_validate!( - r#" + r#" 54 @@ -884,7 +884,7 @@ fn de_subitem_issue_12_with_sub() { } convert_and_validate!( - r#" + r#" 54 @@ -911,7 +911,7 @@ fn de_subitem_issue_12_attributes() { } convert_and_validate!( - r#" + r#" @@ -940,7 +940,7 @@ fn de_subitem_issue_12_attributes_with_sub() { } convert_and_validate!( - r#" + r#" diff --git a/yaserde/tests/option.rs b/yaserde/tests/option.rs index fac7804..1cad3e2 100644 --- a/yaserde/tests/option.rs +++ b/yaserde/tests/option.rs @@ -144,7 +144,7 @@ mod tests { #[test] fn deserialize_without_car() { - let person = r#" + let person = r#" brown 25