support root flatten deserialization

This commit is contained in:
Marc-Antoine Arnaud 2020-04-19 10:57:40 +02:00
parent 7b53515736
commit 856e271f7f
10 changed files with 297 additions and 210 deletions

166
yaserde/tests/de_flatten.rs Normal file
View File

@ -0,0 +1,166 @@
#[macro_use]
extern crate yaserde_derive;
use std::io::Read;
use yaserde::de::from_str;
use yaserde::YaDeserialize;
macro_rules! convert_and_validate {
($content: expr, $struct: tt, $model: expr) => {
let loaded: Result<$struct, String> = from_str($content);
assert_eq!(loaded, Ok($model));
};
}
#[test]
fn de_root_flatten_struct() {
#[derive(Default, PartialEq, Debug, YaDeserialize)]
#[yaserde(flatten)]
struct Content {
binary_data: String,
string_data: String,
}
let content = r#"
<?xml version="1.0" encoding="utf-8"?>
<binary_data>binary</binary_data>
<string_data>string</string_data>
"#;
convert_and_validate!(
content,
Content,
Content {
binary_data: "binary".to_string(),
string_data: "string".to_string(),
}
);
}
#[test]
fn de_root_flatten_enum() {
#[derive(PartialEq, Debug, YaDeserialize)]
#[yaserde(flatten)]
pub enum Content {
Binary(Binary),
Data(Data),
Unknown,
}
impl Default for Content {
fn default() -> Self {
Content::Unknown
}
}
#[derive(Default, PartialEq, Debug, YaDeserialize)]
pub struct Binary {
binary_data: String,
}
#[derive(Default, PartialEq, Debug, YaDeserialize)]
pub struct Data {
string_data: String,
}
let content = r#"
<?xml version="1.0" encoding="utf-8"?>
<Binary>
<binary_data>binary</binary_data>
</Binary>
"#;
convert_and_validate!(
content,
Content,
Content::Binary(Binary {
binary_data: "binary".to_string(),
})
);
}
#[test]
fn de_flatten() {
#[derive(Default, PartialEq, Debug, YaDeserialize)]
struct DateTime {
#[yaserde(flatten)]
date: Date,
time: String,
#[yaserde(flatten)]
kind: DateKind,
}
#[derive(Default, PartialEq, Debug, YaDeserialize)]
struct Date {
year: i32,
month: i32,
day: i32,
#[yaserde(flatten)]
extra: Extra,
#[yaserde(flatten)]
optional_extra: Option<OptionalExtra>,
}
#[derive(Default, PartialEq, Debug, YaDeserialize)]
pub struct Extra {
week: i32,
century: i32,
}
#[derive(Default, PartialEq, Debug, YaDeserialize)]
pub struct OptionalExtra {
lunar_day: i32,
}
#[derive(PartialEq, Debug, YaDeserialize)]
pub enum DateKind {
#[yaserde(rename = "holidays")]
Holidays(Vec<String>),
#[yaserde(rename = "working")]
Working,
}
impl Default for DateKind {
fn default() -> Self {
DateKind::Working
}
};
let content = r#"
<?xml version="1.0" encoding="utf-8"?>
<DateTime>
<year>2020</year>
<month>1</month>
<day>1</day>
<week>1</week>
<century>21</century>
<lunar_day>1</lunar_day>
<time>10:40:03</time>
<holidays>New Year's Day</holidays>
<holidays>Novy God Day</holidays>
<holidays>Polar Bear Swim Day</holidays>
</DateTime>
"#;
convert_and_validate!(
content,
DateTime,
DateTime {
date: Date {
year: 2020,
month: 1,
day: 1,
extra: Extra {
week: 1,
century: 21,
},
optional_extra: Some(OptionalExtra { lunar_day: 1 }),
},
time: "10:40:03".to_string(),
kind: DateKind::Holidays(vec![
"New Year's Day".into(),
"Novy God Day".into(),
"Polar Bear Swim Day".into()
])
}
);
}

View File

@ -691,92 +691,6 @@ fn de_custom() {
); );
} }
#[test]
fn de_flatten() {
#[derive(Default, PartialEq, Debug, YaDeserialize)]
struct DateTime {
#[yaserde(flatten)]
date: Date,
time: String,
#[yaserde(flatten)]
kind: DateKind,
}
#[derive(Default, PartialEq, Debug, YaDeserialize)]
struct Date {
year: i32,
month: i32,
day: i32,
#[yaserde(flatten)]
extra: Extra,
#[yaserde(flatten)]
optional_extra: Option<OptionalExtra>,
}
#[derive(Default, PartialEq, Debug, YaDeserialize)]
pub struct Extra {
week: i32,
century: i32,
}
#[derive(Default, PartialEq, Debug, YaDeserialize)]
pub struct OptionalExtra {
lunar_day: i32,
}
#[derive(PartialEq, Debug, YaDeserialize)]
pub enum DateKind {
#[yaserde(rename = "holidays")]
Holidays(Vec<String>),
#[yaserde(rename = "working")]
Working,
}
impl Default for DateKind {
fn default() -> Self {
DateKind::Working
}
};
let content = r#"
<?xml version="1.0" encoding="utf-8"?>
<DateTime>
<year>2020</year>
<month>1</month>
<day>1</day>
<week>1</week>
<century>21</century>
<lunar_day>1</lunar_day>
<time>10:40:03</time>
<holidays>New Year's Day</holidays>
<holidays>Novy God Day</holidays>
<holidays>Polar Bear Swim Day</holidays>
</DateTime>
"#;
convert_and_validate!(
content,
DateTime,
DateTime {
date: Date {
year: 2020,
month: 1,
day: 1,
extra: Extra {
week: 1,
century: 21,
},
optional_extra: Some(OptionalExtra { lunar_day: 1 }),
},
time: "10:40:03".to_string(),
kind: DateKind::Holidays(vec![
"New Year's Day".into(),
"Novy God Day".into(),
"Polar Bear Swim Day".into()
])
}
);
}
#[test] #[test]
fn de_subitem_issue_12() { fn de_subitem_issue_12() {
#[derive(Default, PartialEq, Debug, YaDeserialize)] #[derive(Default, PartialEq, Debug, YaDeserialize)]

View File

@ -21,8 +21,91 @@ macro_rules! convert_and_validate {
} }
#[test] #[test]
fn ser_root_flatten_struct() { fn ser_flatten() {
#[derive(Default, PartialEq, Debug, YaSerialize)]
struct DateTime {
#[yaserde(flatten)]
date: Date,
time: String,
#[yaserde(flatten)]
kind: DateKind,
}
#[derive(Default, PartialEq, Debug, YaSerialize)]
struct Date {
year: i32,
month: i32,
day: i32,
#[yaserde(flatten)]
extra: Extra,
#[yaserde(flatten)]
optional_extra: Option<OptionalExtra>,
}
#[derive(Default, PartialEq, Debug, YaSerialize)]
pub struct Extra {
week: i32,
century: i32,
}
#[derive(Default, PartialEq, Debug, YaSerialize)]
pub struct OptionalExtra {
lunar_day: i32,
}
#[derive(PartialEq, Debug, YaSerialize)]
pub enum DateKind {
#[yaserde(rename = "holidays")]
Holidays(Vec<String>),
#[yaserde(rename = "working")]
Working,
}
impl Default for DateKind {
fn default() -> Self {
DateKind::Working
}
};
let model = DateTime {
date: Date {
year: 2020,
month: 1,
day: 1,
extra: Extra {
week: 1,
century: 21,
},
optional_extra: Some(OptionalExtra { lunar_day: 1 }),
},
time: "10:40:03".to_string(),
kind: DateKind::Holidays(vec![
"New Year's Day".into(),
"Novy God Day".into(),
"Polar Bear Swim Day".into(),
]),
};
let content = r#"
<?xml version="1.0" encoding="utf-8"?>
<DateTime>
<year>2020</year>
<month>1</month>
<day>1</day>
<week>1</week>
<century>21</century>
<lunar_day>1</lunar_day>
<time>10:40:03</time>
<holidays>New Year's Day</holidays>
<holidays>Novy God Day</holidays>
<holidays>Polar Bear Swim Day</holidays>
</DateTime>"#;
convert_and_validate!(model, content);
}
#[test]
fn ser_root_flatten_struct() {
#[derive(YaSerialize, PartialEq, Debug)] #[derive(YaSerialize, PartialEq, Debug)]
#[yaserde(flatten)] #[yaserde(flatten)]
pub struct Content { pub struct Content {
@ -30,7 +113,7 @@ fn ser_root_flatten_struct() {
string_data: String, string_data: String,
} }
let model = Content{ let model = Content {
binary_data: "binary".to_string(), binary_data: "binary".to_string(),
string_data: "string".to_string(), string_data: "string".to_string(),
}; };
@ -57,12 +140,17 @@ fn ser_root_flatten_enum() {
string_data: String, string_data: String,
} }
let model = Content::Binary(Binary{binary_data: "binary".to_string()}); let model = Content::Binary(Binary {
let content = r#"<?xml version="1.0" encoding="utf-8"?><Binary><binary_data>binary</binary_data></Binary>"#; binary_data: "binary".to_string(),
});
let content =
r#"<?xml version="1.0" encoding="utf-8"?><Binary><binary_data>binary</binary_data></Binary>"#;
convert_and_validate!(model, content); convert_and_validate!(model, content);
let model = Content::Data(Data {
let model = Content::Data(Data{string_data: "string".to_string()}); string_data: "string".to_string(),
let content = r#"<?xml version="1.0" encoding="utf-8"?><Data><string_data>string</string_data></Data>"#; });
let content =
r#"<?xml version="1.0" encoding="utf-8"?><Data><string_data>string</string_data></Data>"#;
convert_and_validate!(model, content); convert_and_validate!(model, content);
} }

View File

@ -329,87 +329,3 @@ fn ser_custom() {
let content = "<?xml version=\"1.0\" encoding=\"utf-8\"?><Date><Year>2020</Year><Month>1</Month><DoubleDay>10</DoubleDay></Date>"; let content = "<?xml version=\"1.0\" encoding=\"utf-8\"?><Date><Year>2020</Year><Month>1</Month><DoubleDay>10</DoubleDay></Date>";
convert_and_validate!(model, content); convert_and_validate!(model, content);
} }
#[test]
fn ser_flatten() {
#[derive(Default, PartialEq, Debug, YaSerialize)]
struct DateTime {
#[yaserde(flatten)]
date: Date,
time: String,
#[yaserde(flatten)]
kind: DateKind,
}
#[derive(Default, PartialEq, Debug, YaSerialize)]
struct Date {
year: i32,
month: i32,
day: i32,
#[yaserde(flatten)]
extra: Extra,
#[yaserde(flatten)]
optional_extra: Option<OptionalExtra>,
}
#[derive(Default, PartialEq, Debug, YaSerialize)]
pub struct Extra {
week: i32,
century: i32,
}
#[derive(Default, PartialEq, Debug, YaSerialize)]
pub struct OptionalExtra {
lunar_day: i32,
}
#[derive(PartialEq, Debug, YaSerialize)]
pub enum DateKind {
#[yaserde(rename = "holidays")]
Holidays(Vec<String>),
#[yaserde(rename = "working")]
Working,
}
impl Default for DateKind {
fn default() -> Self {
DateKind::Working
}
};
let model = DateTime {
date: Date {
year: 2020,
month: 1,
day: 1,
extra: Extra {
week: 1,
century: 21,
},
optional_extra: Some(OptionalExtra { lunar_day: 1 }),
},
time: "10:40:03".to_string(),
kind: DateKind::Holidays(vec![
"New Year's Day".into(),
"Novy God Day".into(),
"Polar Bear Swim Day".into(),
]),
};
let content = r#"
<?xml version="1.0" encoding="utf-8"?>
<DateTime>
<year>2020</year>
<month>1</month>
<day>1</day>
<week>1</week>
<century>21</century>
<lunar_day>1</lunar_day>
<time>10:40:03</time>
<holidays>New Year's Day</holidays>
<holidays>Novy God Day</holidays>
<holidays>Polar Bear Swim Day</holidays>
</DateTime>"#;
convert_and_validate!(model, content);
}

View File

@ -1,7 +1,6 @@
use crate::attribute::*; use crate::attribute::*;
use crate::field_type::*; use crate::field_type::*;
use proc_macro2::TokenStream; use proc_macro2::TokenStream;
use std::collections::BTreeMap;
use syn::spanned::Spanned; use syn::spanned::Spanned;
use syn::DataEnum; use syn::DataEnum;
use syn::Fields; use syn::Fields;
@ -11,7 +10,7 @@ pub fn parse(
data_enum: &DataEnum, data_enum: &DataEnum,
name: &Ident, name: &Ident,
root: &str, root: &str,
_namespaces: &BTreeMap<String, String>, root_attributes: &YaSerdeAttribute,
) -> TokenStream { ) -> TokenStream {
let match_to_enum: TokenStream = data_enum let match_to_enum: TokenStream = data_enum
.variants .variants
@ -20,6 +19,8 @@ pub fn parse(
.filter_map(|f| f) .filter_map(|f| f)
.collect(); .collect();
let flatten = root_attributes.flatten;
quote! { quote! {
use xml::reader::XmlEvent; use xml::reader::XmlEvent;
use yaserde::Visitor; use yaserde::Visitor;
@ -47,7 +48,7 @@ pub fn parse(
match name.local_name.as_str() { match name.local_name.as_str() {
#match_to_enum #match_to_enum
named_element => { _named_element => {
let _root = reader.next_event(); let _root = reader.next_event();
} }
} }
@ -68,6 +69,13 @@ pub fn parse(
XmlEvent::Characters(ref text_content) => { XmlEvent::Characters(ref text_content) => {
let _root = reader.next_event(); let _root = reader.next_event();
} }
XmlEvent::EndDocument => {
if #flatten {
break;
}
return Err(format!("End of document, missing some content ?"))
}
event => { event => {
return Err(format!("unknown event {:?}", event)) return Err(format!("unknown event {:?}", event))
} }

View File

@ -11,13 +11,13 @@ pub fn parse(
data_struct: &DataStruct, data_struct: &DataStruct,
name: &Ident, name: &Ident,
root: &str, root: &str,
prefix: &Option<String>, root_attributes: &YaSerdeAttribute,
namespaces: &BTreeMap<String, String>,
) -> TokenStream { ) -> TokenStream {
let namespaces_matches: TokenStream = namespaces let namespaces_matches: TokenStream = root_attributes
.namespaces
.iter() .iter()
.map(|(p, ns)| { .map(|(p, ns)| {
if prefix.as_ref() == Some(p) { if root_attributes.prefix.as_ref() == Some(p) {
Some(quote!(#ns => {})) Some(quote!(#ns => {}))
} else { } else {
None None
@ -199,7 +199,7 @@ pub fn parse(
&action, &action,
&field_attrs, &field_attrs,
label, label,
&namespaces, &root_attributes.namespaces,
field.span(), field.span(),
) )
}; };
@ -368,6 +368,8 @@ pub fn parse(
build_code_for_unused_xml_events(&call_flatten_visitors) build_code_for_unused_xml_events(&call_flatten_visitors)
}; };
let flatten = root_attributes.flatten;
quote! { quote! {
use xml::reader::{XmlEvent, EventReader}; use xml::reader::{XmlEvent, EventReader};
use xml::writer::EventWriter; use xml::writer::EventWriter;
@ -407,7 +409,6 @@ pub fn parse(
loop { loop {
let event = reader.peek()?.to_owned(); let event = reader.peek()?.to_owned();
match event { match event {
XmlEvent::StartElement{ref name, ref attributes, ..} => { XmlEvent::StartElement{ref name, ref attributes, ..} => {
let mut skipped = false; let mut skipped = false;
@ -443,6 +444,11 @@ pub fn parse(
#write_unused #write_unused
depth -= 1; depth -= 1;
} }
XmlEvent::EndDocument => {
if #flatten {
break;
}
}
XmlEvent::Characters(ref text_content) => { XmlEvent::Characters(ref text_content) => {
#set_text #set_text
let event = reader.next_event()?; let event = reader.next_event()?;

View File

@ -16,16 +16,10 @@ pub fn expand_derive_deserialize(ast: &syn::DeriveInput) -> Result<TokenStream,
let root = root_attrs.clone().root.unwrap_or_else(|| name.to_string()); let root = root_attrs.clone().root.unwrap_or_else(|| name.to_string());
let impl_block = match *data { let impl_block = match *data {
syn::Data::Struct(ref data_struct) => expand_struct::parse( syn::Data::Struct(ref data_struct) => {
data_struct, expand_struct::parse(data_struct, name, &root, &root_attrs)
name,
&root,
&root_attrs.prefix,
&root_attrs.namespaces,
),
syn::Data::Enum(ref data_enum) => {
expand_enum::parse(data_enum, name, &root, &root_attrs.namespaces)
} }
syn::Data::Enum(ref data_enum) => expand_enum::parse(data_enum, name, &root, &root_attrs),
syn::Data::Union(ref _data_union) => unimplemented!(), syn::Data::Union(ref _data_union) => unimplemented!(),
}; };

View File

@ -222,7 +222,8 @@ pub fn serialize(
.filter_map(|x| x) .filter_map(|x| x)
.collect(); .collect();
let add_namespaces: TokenStream = root_attributes.namespaces let add_namespaces: TokenStream = root_attributes
.namespaces
.iter() .iter()
.map(|(prefix, namespace)| { .map(|(prefix, namespace)| {
if let Some(dn) = &root_attributes.default_namespace { if let Some(dn) = &root_attributes.default_namespace {

View File

@ -203,7 +203,8 @@ pub fn serialize(
.filter_map(|x| x) .filter_map(|x| x)
.collect(); .collect();
let add_namespaces: TokenStream = root_attributes.namespaces let add_namespaces: TokenStream = root_attributes
.namespaces
.iter() .iter()
.map(|(prefix, namespace)| { .map(|(prefix, namespace)| {
if let Some(dn) = &root_attributes.default_namespace { if let Some(dn) = &root_attributes.default_namespace {

View File

@ -18,7 +18,8 @@ pub fn expand_derive_serialize(ast: &syn::DeriveInput) -> Result<TokenStream, St
let prefix = if root_attrs.default_namespace == root_attrs.prefix { let prefix = if root_attrs.default_namespace == root_attrs.prefix {
"".to_string() "".to_string()
} else { } else {
root_attrs.clone() root_attrs
.clone()
.prefix .prefix
.map_or("".to_string(), |prefix| prefix + ":") .map_or("".to_string(), |prefix| prefix + ":")
}; };
@ -26,18 +27,10 @@ pub fn expand_derive_serialize(ast: &syn::DeriveInput) -> Result<TokenStream, St
let root = format!("{}{}", prefix, root); let root = format!("{}{}", prefix, root);
let impl_block = match *data { let impl_block = match *data {
syn::Data::Struct(ref data_struct) => expand_struct::serialize( syn::Data::Struct(ref data_struct) => {
data_struct, expand_struct::serialize(data_struct, name, &root, &root_attrs)
name, }
&root, syn::Data::Enum(ref data_enum) => expand_enum::serialize(data_enum, name, &root, &root_attrs),
&root_attrs,
),
syn::Data::Enum(ref data_enum) => expand_enum::serialize(
data_enum,
name,
&root,
&root_attrs,
),
syn::Data::Union(ref _data_union) => unimplemented!(), syn::Data::Union(ref _data_union) => unimplemented!(),
}; };