diff --git a/yaserde/tests/de_flatten.rs b/yaserde/tests/de_flatten.rs
new file mode 100644
index 0000000..d385430
--- /dev/null
+++ b/yaserde/tests/de_flatten.rs
@@ -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#"
+
+ binary
+ string
+ "#;
+
+ 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#"
+
+
+ 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,
+ }
+
+ #[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),
+ #[yaserde(rename = "working")]
+ Working,
+ }
+
+ impl Default for DateKind {
+ fn default() -> Self {
+ DateKind::Working
+ }
+ };
+
+ let content = r#"
+
+
+ 2020
+ 1
+ 1
+ 1
+ 21
+ 1
+
+ New Year's Day
+ Novy God Day
+ Polar Bear Swim Day
+
+ "#;
+ 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()
+ ])
+ }
+ );
+}
diff --git a/yaserde/tests/deserializer.rs b/yaserde/tests/deserializer.rs
index 21df4c0..dd212ce 100644
--- a/yaserde/tests/deserializer.rs
+++ b/yaserde/tests/deserializer.rs
@@ -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,
- }
-
- #[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),
- #[yaserde(rename = "working")]
- Working,
- }
-
- impl Default for DateKind {
- fn default() -> Self {
- DateKind::Working
- }
- };
-
- let content = r#"
-
-
- 2020
- 1
- 1
- 1
- 21
- 1
-
- New Year's Day
- Novy God Day
- Polar Bear Swim Day
-
- "#;
- 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]
fn de_subitem_issue_12() {
#[derive(Default, PartialEq, Debug, YaDeserialize)]
diff --git a/yaserde/tests/ser_flatten.rs b/yaserde/tests/ser_flatten.rs
new file mode 100644
index 0000000..0127ed1
--- /dev/null
+++ b/yaserde/tests/ser_flatten.rs
@@ -0,0 +1,156 @@
+#[macro_use]
+extern crate yaserde_derive;
+
+use std::io::Write;
+use yaserde::ser::to_string;
+use yaserde::YaSerialize;
+
+macro_rules! convert_and_validate {
+ ($model: expr, $content: expr) => {
+ let data: Result = to_string(&$model);
+ assert_eq!(
+ data,
+ Ok(
+ String::from($content)
+ .split("\n")
+ .map(|s| s.trim())
+ .collect::()
+ )
+ );
+ };
+}
+
+#[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,
+ }
+
+ #[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),
+ #[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#"
+
+
+ 2020
+ 1
+ 1
+ 1
+ 21
+ 1
+
+ New Year's Day
+ Novy God Day
+ Polar Bear Swim Day
+ "#;
+
+ convert_and_validate!(model, content);
+}
+
+#[test]
+fn ser_root_flatten_struct() {
+ #[derive(YaSerialize, PartialEq, Debug)]
+ #[yaserde(flatten)]
+ pub struct Content {
+ binary_data: String,
+ string_data: String,
+ }
+
+ let model = Content {
+ binary_data: "binary".to_string(),
+ string_data: "string".to_string(),
+ };
+ let content = r#"binarystring"#;
+ convert_and_validate!(model, content);
+}
+
+#[test]
+fn ser_root_flatten_enum() {
+ #[derive(YaSerialize, PartialEq, Debug)]
+ #[yaserde(flatten)]
+ pub enum Content {
+ Binary(Binary),
+ Data(Data),
+ }
+
+ #[derive(YaSerialize, PartialEq, Debug)]
+ pub struct Binary {
+ binary_data: String,
+ }
+
+ #[derive(YaSerialize, PartialEq, Debug)]
+ pub struct Data {
+ string_data: String,
+ }
+
+ let model = Content::Binary(Binary {
+ binary_data: "binary".to_string(),
+ });
+ let content =
+ r#"binary"#;
+ convert_and_validate!(model, content);
+
+ let model = Content::Data(Data {
+ string_data: "string".to_string(),
+ });
+ let content =
+ r#"string"#;
+ convert_and_validate!(model, content);
+}
diff --git a/yaserde/tests/serializer.rs b/yaserde/tests/serializer.rs
index 0e1e8a1..c3ff11b 100644
--- a/yaserde/tests/serializer.rs
+++ b/yaserde/tests/serializer.rs
@@ -329,87 +329,3 @@ fn ser_custom() {
let content = "2020110";
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,
- }
-
- #[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),
- #[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#"
-
-
- 2020
- 1
- 1
- 1
- 21
- 1
-
- New Year's Day
- Novy God Day
- Polar Bear Swim Day
- "#;
-
- convert_and_validate!(model, content);
-}
diff --git a/yaserde_derive/src/de/expand_enum.rs b/yaserde_derive/src/de/expand_enum.rs
index 4fdb0ed..c43d246 100644
--- a/yaserde_derive/src/de/expand_enum.rs
+++ b/yaserde_derive/src/de/expand_enum.rs
@@ -1,7 +1,6 @@
use crate::attribute::*;
use crate::field_type::*;
use proc_macro2::TokenStream;
-use std::collections::BTreeMap;
use syn::spanned::Spanned;
use syn::DataEnum;
use syn::Fields;
@@ -11,7 +10,7 @@ pub fn parse(
data_enum: &DataEnum,
name: &Ident,
root: &str,
- _namespaces: &BTreeMap,
+ root_attributes: &YaSerdeAttribute,
) -> TokenStream {
let match_to_enum: TokenStream = data_enum
.variants
@@ -20,6 +19,8 @@ pub fn parse(
.filter_map(|f| f)
.collect();
+ let flatten = root_attributes.flatten;
+
quote! {
use xml::reader::XmlEvent;
use yaserde::Visitor;
@@ -47,7 +48,7 @@ pub fn parse(
match name.local_name.as_str() {
#match_to_enum
- named_element => {
+ _named_element => {
let _root = reader.next_event();
}
}
@@ -68,6 +69,13 @@ pub fn parse(
XmlEvent::Characters(ref text_content) => {
let _root = reader.next_event();
}
+ XmlEvent::EndDocument => {
+ if #flatten {
+ break;
+ }
+
+ return Err(format!("End of document, missing some content ?"))
+ }
event => {
return Err(format!("unknown event {:?}", event))
}
diff --git a/yaserde_derive/src/de/expand_struct.rs b/yaserde_derive/src/de/expand_struct.rs
index 1f1d51e..3c48215 100644
--- a/yaserde_derive/src/de/expand_struct.rs
+++ b/yaserde_derive/src/de/expand_struct.rs
@@ -11,13 +11,13 @@ pub fn parse(
data_struct: &DataStruct,
name: &Ident,
root: &str,
- prefix: &Option,
- namespaces: &BTreeMap,
+ root_attributes: &YaSerdeAttribute,
) -> TokenStream {
- let namespaces_matches: TokenStream = namespaces
+ let namespaces_matches: TokenStream = root_attributes
+ .namespaces
.iter()
.map(|(p, ns)| {
- if prefix.as_ref() == Some(p) {
+ if root_attributes.prefix.as_ref() == Some(p) {
Some(quote!(#ns => {}))
} else {
None
@@ -199,7 +199,7 @@ pub fn parse(
&action,
&field_attrs,
label,
- &namespaces,
+ &root_attributes.namespaces,
field.span(),
)
};
@@ -368,6 +368,8 @@ pub fn parse(
build_code_for_unused_xml_events(&call_flatten_visitors)
};
+ let flatten = root_attributes.flatten;
+
quote! {
use xml::reader::{XmlEvent, EventReader};
use xml::writer::EventWriter;
@@ -407,7 +409,6 @@ pub fn parse(
loop {
let event = reader.peek()?.to_owned();
-
match event {
XmlEvent::StartElement{ref name, ref attributes, ..} => {
let mut skipped = false;
@@ -443,6 +444,11 @@ pub fn parse(
#write_unused
depth -= 1;
}
+ XmlEvent::EndDocument => {
+ if #flatten {
+ break;
+ }
+ }
XmlEvent::Characters(ref text_content) => {
#set_text
let event = reader.next_event()?;
diff --git a/yaserde_derive/src/de/mod.rs b/yaserde_derive/src/de/mod.rs
index 4e3e8ab..84f1f17 100644
--- a/yaserde_derive/src/de/mod.rs
+++ b/yaserde_derive/src/de/mod.rs
@@ -16,16 +16,10 @@ pub fn expand_derive_deserialize(ast: &syn::DeriveInput) -> Result expand_struct::parse(
- data_struct,
- 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::Struct(ref data_struct) => {
+ expand_struct::parse(data_struct, name, &root, &root_attrs)
}
+ syn::Data::Enum(ref data_enum) => expand_enum::parse(data_enum, name, &root, &root_attrs),
syn::Data::Union(ref _data_union) => unimplemented!(),
};
diff --git a/yaserde_derive/src/ser/expand_enum.rs b/yaserde_derive/src/ser/expand_enum.rs
index 2a63988..a357272 100644
--- a/yaserde_derive/src/ser/expand_enum.rs
+++ b/yaserde_derive/src/ser/expand_enum.rs
@@ -1,7 +1,6 @@
use crate::attribute::*;
use crate::field_type::*;
use proc_macro2::TokenStream;
-use std::collections::BTreeMap;
use syn::spanned::Spanned;
use syn::DataEnum;
use syn::Fields;
@@ -11,8 +10,7 @@ pub fn serialize(
data_enum: &DataEnum,
name: &Ident,
root: &str,
- namespaces: &BTreeMap,
- default_namespace: &Option,
+ root_attributes: &YaSerdeAttribute,
) -> TokenStream {
let write_enum_content: TokenStream = data_enum
.variants
@@ -224,10 +222,11 @@ pub fn serialize(
.filter_map(|x| x)
.collect();
- let add_namespaces: TokenStream = namespaces
+ let add_namespaces: TokenStream = root_attributes
+ .namespaces
.iter()
.map(|(prefix, namespace)| {
- if let Some(dn) = default_namespace {
+ if let Some(dn) = &root_attributes.default_namespace {
if dn == prefix {
return Some(quote!(
.default_ns(#namespace)
@@ -241,6 +240,8 @@ pub fn serialize(
.filter_map(|x| x)
.collect();
+ let flatten = root_attributes.flatten;
+
quote! {
use xml::writer::XmlEvent;
@@ -250,7 +251,7 @@ pub fn serialize(
-> Result<(), String> {
let skip = writer.skip_start_end();
- if !skip {
+ if !#flatten && !skip {
if let Some(label) = writer.get_start_event_name() {
let struct_start_event = XmlEvent::start_element(label.as_ref());
writer.write(struct_start_event).map_err(|e| e.to_string())?;
@@ -264,7 +265,7 @@ pub fn serialize(
#write_enum_content
}
- if !skip {
+ if !#flatten && !skip {
let struct_end_event = XmlEvent::end_element();
writer.write(struct_end_event).map_err(|e| e.to_string())?;
}
diff --git a/yaserde_derive/src/ser/expand_struct.rs b/yaserde_derive/src/ser/expand_struct.rs
index 1411a03..83bbd19 100644
--- a/yaserde_derive/src/ser/expand_struct.rs
+++ b/yaserde_derive/src/ser/expand_struct.rs
@@ -2,7 +2,6 @@ use crate::attribute::*;
use crate::field_type::*;
use crate::ser::element::*;
use proc_macro2::TokenStream;
-use std::collections::BTreeMap;
use std::string::ToString;
use syn::spanned::Spanned;
use syn::DataStruct;
@@ -12,8 +11,7 @@ pub fn serialize(
data_struct: &DataStruct,
name: &Ident,
root: &str,
- namespaces: &BTreeMap,
- default_namespace: &Option,
+ root_attributes: &YaSerdeAttribute,
) -> TokenStream {
let build_attributes: TokenStream = data_struct
.fields
@@ -26,7 +24,7 @@ pub fn serialize(
let label = &field.ident;
- let label_name = build_label_name(&field, &field_attrs, default_namespace);
+ let label_name = build_label_name(&field, &field_attrs, &root_attributes.default_namespace);
get_field_type(field).and_then(|f| match f {
FieldType::FieldTypeString
@@ -205,10 +203,11 @@ pub fn serialize(
.filter_map(|x| x)
.collect();
- let add_namespaces: TokenStream = namespaces
+ let add_namespaces: TokenStream = root_attributes
+ .namespaces
.iter()
.map(|(prefix, namespace)| {
- if let Some(dn) = default_namespace {
+ if let Some(dn) = &root_attributes.default_namespace {
if dn == prefix {
return Some(quote!(
.default_ns(#namespace)
@@ -239,7 +238,7 @@ pub fn serialize(
));
}
- let label_name = build_label_name(&field, &field_attrs, default_namespace);
+ let label_name = build_label_name(&field, &field_attrs, &root_attributes.default_namespace);
let conditions = condition_generator(label, &field_attrs);
get_field_type(field).and_then(|f| match f {
@@ -381,6 +380,8 @@ pub fn serialize(
.filter_map(|x| x)
.collect();
+ let flatten = root_attributes.flatten;
+
quote! {
use xml::writer::XmlEvent;
@@ -390,7 +391,7 @@ pub fn serialize(
-> Result<(), String> {
let skip = writer.skip_start_end();
- if !skip {
+ if !#flatten && !skip {
let yaserde_label = writer.get_start_event_name().unwrap_or_else(|| #root.to_string());
let struct_start_event = XmlEvent::start_element(yaserde_label.as_ref())#add_namespaces;
#build_attributes
@@ -399,7 +400,7 @@ pub fn serialize(
#struct_inspector
- if !skip {
+ if !#flatten && !skip {
let struct_end_event = XmlEvent::end_element();
writer.write(struct_end_event).map_err(|e| e.to_string())?;
}
diff --git a/yaserde_derive/src/ser/mod.rs b/yaserde_derive/src/ser/mod.rs
index 68a1532..dec8dfa 100644
--- a/yaserde_derive/src/ser/mod.rs
+++ b/yaserde_derive/src/ser/mod.rs
@@ -2,7 +2,7 @@ pub mod element;
pub mod expand_enum;
pub mod expand_struct;
-use crate::attribute;
+use crate::attribute::YaSerdeAttribute;
use proc_macro2::TokenStream;
use syn;
use syn::Ident;
@@ -12,13 +12,14 @@ pub fn expand_derive_serialize(ast: &syn::DeriveInput) -> Result Result expand_struct::serialize(
- data_struct,
- name,
- &root,
- &root_attrs.namespaces,
- &root_attrs.default_namespace,
- ),
- syn::Data::Enum(ref data_enum) => expand_enum::serialize(
- data_enum,
- name,
- &root,
- &root_attrs.namespaces,
- &root_attrs.default_namespace,
- ),
+ syn::Data::Struct(ref data_struct) => {
+ expand_struct::serialize(data_struct, name, &root, &root_attrs)
+ }
+ syn::Data::Enum(ref data_enum) => expand_enum::serialize(data_enum, name, &root, &root_attrs),
syn::Data::Union(ref _data_union) => unimplemented!(),
};