start to support skip serializing if feature

issue #43
This commit is contained in:
Marc-Antoine Arnaud 2020-02-25 13:35:13 +01:00
parent 1a67c4907d
commit 4e03b57723
6 changed files with 182 additions and 120 deletions

View File

@ -183,7 +183,7 @@ fn ser_struct_default_namespace_via_attribute() {
}
#[test]
fn de_struct_namespace_nested() {
fn ser_struct_namespace_nested() {
#[derive(YaSerialize, Default, PartialEq, Debug)]
#[yaserde(prefix = "nsa", namespace = "nsa: http://www.sample.com/ns/a")]
struct A {

68
yaserde/tests/ser_skip.rs Normal file
View File

@ -0,0 +1,68 @@
extern crate log;
extern crate xml;
extern crate yaserde;
#[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<String, String> = to_string(&$model);
assert_eq!(
data,
Ok(
String::from($content)
.split("\n")
.map(|s| s.trim())
.collect::<String>()
)
);
};
}
#[test]
fn ser_skip_serializing_if_for_struct() {
#[derive(YaSerialize, PartialEq, Debug)]
#[yaserde(root = "base")]
pub struct XmlStruct {
#[yaserde(skip_serializing_if = "check_string_function")]
string_item: String,
#[yaserde(skip_serializing_if = "check_bool_function")]
bool_item: bool,
#[yaserde(skip_serializing_if = "check_f32_function")]
f32_item: f32,
#[yaserde(skip_serializing_if = "check_option_string_function")]
option_string_item: Option<String>,
}
impl XmlStruct {
fn check_string_function(&self, value: &String) -> bool {
value == "something"
}
fn check_option_string_function(&self, value: &Option<String>) -> bool {
value == &Some("something".to_string())
}
fn check_bool_function(&self, value: &bool) -> bool {
value == &true
}
fn check_f32_function(&self, value: &f32) -> bool {
value == &0.0
}
}
let model = XmlStruct {
string_item: "something".to_string(),
bool_item: true,
f32_item: 0.0,
option_string_item: Some("something".to_string()),
};
let content = "<?xml version=\"1.0\" encoding=\"utf-8\"?><base />";
convert_and_validate!(model, content);
}

View File

@ -14,6 +14,7 @@ pub struct YaSerdeAttribute {
pub prefix: Option<String>,
pub root: Option<String>,
pub rename: Option<String>,
pub skip_serializing_if: Option<String>,
pub text: bool,
}
@ -41,6 +42,7 @@ impl YaSerdeAttribute {
let mut prefix = None;
let mut rename = None;
let mut root = None;
let mut skip_serializing_if = None;
let mut text = false;
for attr in attrs.iter() {
@ -85,6 +87,9 @@ impl YaSerdeAttribute {
"root" => {
root = get_value(&mut attr_iter);
}
"skip_serializing_if" => {
skip_serializing_if = get_value(&mut attr_iter);
}
"text" => {
text = true;
}
@ -106,6 +111,7 @@ impl YaSerdeAttribute {
prefix,
rename,
root,
skip_serializing_if,
text,
}
}
@ -126,6 +132,7 @@ fn parse_empty_attributes() {
prefix: None,
root: None,
rename: None,
skip_serializing_if: None,
text: false,
},
attrs
@ -175,6 +182,7 @@ fn parse_attributes() {
prefix: None,
root: None,
rename: None,
skip_serializing_if: None,
text: false,
},
attrs
@ -228,6 +236,7 @@ fn parse_attributes_with_values() {
prefix: None,
root: None,
rename: None,
skip_serializing_if: None,
text: false,
},
attrs

View File

@ -1,55 +1,50 @@
use attribute::*;
use proc_macro2::{Ident, Span, TokenStream};
pub fn enclose_formatted_characters(label: &Ident, label_name: String) -> TokenStream {
quote! {
let start_event = XmlEvent::start_element(#label_name);
let _ret = writer.write(start_event);
let yas_value = format!("{}", &self.#label);
let data_event = XmlEvent::characters(&yas_value);
let _ret = writer.write(data_event);
let end_event = XmlEvent::end_element();
let _ret = writer.write(end_event);
}
enclose_xml_event(label_name, quote!(format!("{}", &self.#label)))
}
pub fn enclose_formatted_characters_for_value(label: &Ident, label_name: String) -> TokenStream {
quote! {
let start_event = XmlEvent::start_element(#label_name);
let _ret = writer.write(start_event);
let value = format!("{}", #label);
let data_event = XmlEvent::characters(&value);
let _ret = writer.write(data_event);
let end_event = XmlEvent::end_element();
let _ret = writer.write(end_event);
}
enclose_xml_event(label_name, quote!(format!("{}", #label)))
}
pub fn enclose_characters(label: &Option<Ident>, label_name: String) -> TokenStream {
enclose_xml_event(label_name, quote!(format!("{}", self.#label)))
}
pub fn enclose_xml_event(label_name: String, yaserde_format: TokenStream) -> TokenStream {
quote! {
let start_event = XmlEvent::start_element(#label_name);
let _ret = writer.write(start_event);
writer.write(start_event).map_err(|e| e.to_string())?;
let value = format!("{}", self.#label);
let data_event = XmlEvent::characters(&value);
let _ret = writer.write(data_event);
let yaserde_value = #yaserde_format;
let data_event = XmlEvent::characters(&yaserde_value);
writer.write(data_event).map_err(|e| e.to_string())?;
let end_event = XmlEvent::end_element();
let _ret = writer.write(end_event);
writer.write(end_event).map_err(|e| e.to_string())?;
}
}
pub fn serialize_element(
label: &Option<Ident>,
label_name: String,
default: &Option<String>,
conditions: &TokenStream,
) -> Option<TokenStream> {
let inner = enclose_characters(label, label_name);
if let Some(ref d) = default {
Some(quote! {
#conditions {
#inner
}
})
}
pub fn condition_generator(label: &Option<Ident>, attributes: &YaSerdeAttribute) -> TokenStream {
let mut conditions = None;
if let Some(ref d) = attributes.default {
let default_function = Ident::new(
&d,
label
@ -57,14 +52,23 @@ pub fn serialize_element(
.map_or(Span::call_site(), |ident| ident.span()),
);
Some(quote! {
if self.#label != #default_function() {
#inner
}
})
} else {
Some(quote! {
#inner
})
conditions = Some(quote!(self.#label != #default_function()))
}
if let Some(ref s) = attributes.skip_serializing_if {
let skip_if_function = Ident::new(
&s,
label
.as_ref()
.map_or(Span::call_site(), |ident| ident.span()),
);
conditions = if let Some(prev_conditions) = conditions {
Some(quote!(!#skip_if_function() && #prev_conditions))
} else {
Some(quote!(!self.#skip_if_function(&self.#label)))
};
}
conditions.map(|c| quote!(if #c)).unwrap_or(quote!())
}

View File

@ -33,7 +33,7 @@ pub fn serialize(
Fields::Unit => Some(quote! {
&#name::#label => {
let data_event = XmlEvent::characters(#label_name);
let _ret = writer.write(data_event);
writer.write(data_event).map_err(|e| e.to_string())?;
}
}),
Fields::Named(ref fields) => {
@ -50,7 +50,7 @@ pub fn serialize(
if field_attrs.text {
return Some(quote!(
let data_event = XmlEvent::characters(&self.#field_label);
let _ret = writer.write(data_event);
writer.write(data_event).map_err(|e| e.to_string())?;
));
}
@ -61,19 +61,21 @@ pub fn serialize(
let field_label_name = renamed_field_label.unwrap().to_string();
match get_field_type(field) {
Some(FieldType::FieldTypeString) => Some(quote! {
match self {
&#name::#label{ref #field_label, ..} => {
let struct_start_event = XmlEvent::start_element(#field_label_name);
let _ret = writer.write(struct_start_event);
Some(FieldType::FieldTypeString) => Some({
quote! {
match self {
&#name::#label{ref #field_label, ..} => {
let struct_start_event = XmlEvent::start_element(#field_label_name);
writer.write(struct_start_event).map_err(|e| e.to_string())?;
let data_event = XmlEvent::characters(#field_label);
let _ret = writer.write(data_event);
let data_event = XmlEvent::characters(#field_label);
writer.write(data_event).map_err(|e| e.to_string())?;
let struct_end_event = XmlEvent::end_element();
let _ret = writer.write(struct_end_event);
},
_ => {},
let struct_end_event = XmlEvent::end_element();
writer.write(struct_end_event).map_err(|e| e.to_string())?;
},
_ => {},
}
}
}),
Some(FieldType::FieldTypeStruct { .. }) => Some(quote! {
@ -123,24 +125,24 @@ pub fn serialize(
let write_element = |action: &TokenStream| {
quote! {
let struct_start_event = XmlEvent::start_element(#label_name);
let _ret = writer.write(struct_start_event);
writer.write(struct_start_event).map_err(|e| e.to_string())?;
#action
let struct_end_event = XmlEvent::end_element();
let _ret = writer.write(struct_end_event);
writer.write(struct_end_event).map_err(|e| e.to_string())?;
}
};
let write_string_chars = quote! {
let data_event = XmlEvent::characters(item);
let _ret = writer.write(data_event);
writer.write(data_event).map_err(|e| e.to_string())?;
};
let write_simple_type = write_element(&quote! {
let s = item.to_string();
let data_event = XmlEvent::characters(&s);
let _ret = writer.write(data_event);
writer.write(data_event).map_err(|e| e.to_string())?;
});
let serialize = quote! {
@ -239,10 +241,10 @@ pub fn serialize(
if !skip {
if let Some(label) = writer.get_start_event_name() {
let struct_start_event = XmlEvent::start_element(label.as_ref());
let _ret = writer.write(struct_start_event);
writer.write(struct_start_event).map_err(|e| e.to_string())?;
} else {
let struct_start_event = XmlEvent::start_element(#root)#add_namespaces;
let _ret = writer.write(struct_start_event);
writer.write(struct_start_event).map_err(|e| e.to_string())?;
}
}
@ -252,7 +254,7 @@ pub fn serialize(
if !skip {
let struct_end_event = XmlEvent::end_element();
let _ret = writer.write(struct_end_event);
writer.write(struct_end_event).map_err(|e| e.to_string())?;
}
Ok(())

View File

@ -126,7 +126,7 @@ pub fn serialize(
}
}
FieldType::FieldTypeVec { .. } => {
let item_ident = Ident::new("yas_item", field.span());
let item_ident = Ident::new("yaserde_item", field.span());
let inner = enclose_formatted_characters(&item_ident, label_name);
if let Some(ref d) = field_attrs.default {
@ -134,8 +134,8 @@ pub fn serialize(
Some(quote! {
if self.#label != #default_function() {
if let Some(ref yas_list) = self.#label {
for yas_item in yas_list.iter() {
if let Some(ref yaserde_list) = self.#label {
for yaserde_item in yaserde_list.iter() {
#inner
}
}
@ -143,7 +143,7 @@ pub fn serialize(
})
} else {
Some(quote! {
for yas_item in &self.#label {
for yaserde_item in &self.#label {
#inner
}
})
@ -236,11 +236,12 @@ pub fn serialize(
if field_attrs.text {
return Some(quote!(
let data_event = XmlEvent::characters(&self.#label);
let _ret = writer.write(data_event);
writer.write(data_event).map_err(|e| e.to_string())?;
));
}
let label_name = build_label_name(&field, &field_attrs);
let conditions = condition_generator(label, &field_attrs);
get_field_type(field).and_then(|f| match f {
FieldType::FieldTypeString
@ -254,7 +255,7 @@ pub fn serialize(
| FieldType::FieldTypeI64
| FieldType::FieldTypeU64
| FieldType::FieldTypeF32
| FieldType::FieldTypeF64 => serialize_element(label, label_name, &field_attrs.default),
| FieldType::FieldTypeF64 => serialize_element(label, label_name, &conditions),
FieldType::FieldTypeOption { data_type } => match *data_type {
FieldType::FieldTypeString
| FieldType::FieldTypeBool
@ -268,52 +269,30 @@ pub fn serialize(
| FieldType::FieldTypeU64
| FieldType::FieldTypeF32
| FieldType::FieldTypeF64 => {
let item_ident = Ident::new("yas_item", field.span());
let item_ident = Ident::new("yaserde_item", field.span());
let inner = enclose_formatted_characters_for_value(&item_ident, label_name);
if let Some(ref d) = field_attrs.default {
let default_function = Ident::new(&d, field.span());
Some(quote! {
if self.#label != #default_function() {
if let Some(ref yas_item) = self.#label {
#inner
}
}
})
} else {
Some(quote! {
if let Some(ref yas_item) = self.#label {
Some(quote! {
#conditions {
if let Some(ref yaserde_item) = self.#label {
#inner
}
})
}
}
})
}
FieldType::FieldTypeVec { .. } => {
let item_ident = Ident::new("yas_item", field.span());
let item_ident = Ident::new("yaserde_item", field.span());
let inner = enclose_formatted_characters_for_value(&item_ident, label_name);
if let Some(ref d) = field_attrs.default {
let default_function = Ident::new(&d, field.span());
Some(quote! {
if self.#label != #default_function() {
if let Some(ref yas_items) = &self.#label {
for yas_item in yas_items.iter() {
#inner
}
}
}
})
} else {
Some(quote! {
if let Some(ref yas_items) = &self.#label {
for yas_item in yas_items.iter() {
Some(quote! {
#conditions {
if let Some(ref yaserde_items) = &self.#label {
for yaserde_item in yaserde_items.iter() {
#inner
}
}
})
}
}
})
}
FieldType::FieldTypeStruct { .. } => Some(if field_attrs.flatten {
quote! {
@ -334,26 +313,26 @@ pub fn serialize(
}),
_ => unimplemented!(),
},
FieldType::FieldTypeStruct { .. } => Some(if field_attrs.flatten {
quote! {
writer.set_start_event_name(None);
writer.set_skip_start_end(true);
FieldType::FieldTypeStruct { .. } => {
let (start_event, skip_start) = if field_attrs.flatten {
(quote!(None), true)
} else {
(quote!(Some(#label_name.to_string())), false)
};
Some(quote! {
writer.set_start_event_name(#start_event);
writer.set_skip_start_end(#skip_start);
self.#label.serialize(writer)?;
}
} else {
quote! {
writer.set_start_event_name(Some(#label_name.to_string()));
writer.set_skip_start_end(false);
self.#label.serialize(writer)?;
}
}),
})
}
FieldType::FieldTypeVec { data_type } => match *data_type {
FieldType::FieldTypeString => {
let item_ident = Ident::new("yas_item", field.span());
let item_ident = Ident::new("yaserde_item", field.span());
let inner = enclose_formatted_characters_for_value(&item_ident, label_name);
Some(quote! {
for yas_item in &self.#label {
for yaserde_item in &self.#label {
#inner
}
})
@ -369,11 +348,11 @@ pub fn serialize(
| FieldType::FieldTypeU64
| FieldType::FieldTypeF32
| FieldType::FieldTypeF64 => {
let item_ident = Ident::new("yas_item", field.span());
let item_ident = Ident::new("yaserde_item", field.span());
let inner = enclose_formatted_characters_for_value(&item_ident, label_name);
Some(quote! {
for yas_item in &self.#label {
for yaserde_item in &self.#label {
#inner
}
})
@ -413,17 +392,17 @@ pub fn serialize(
let skip = writer.skip_start_end();
if !skip {
let label = writer.get_start_event_name().unwrap_or_else(|| #root.to_string());
let struct_start_event = XmlEvent::start_element(label.as_ref())#add_namespaces;
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
let _ret = writer.write(struct_start_event);
writer.write(struct_start_event).map_err(|e| e.to_string())?;
}
#struct_inspector
if !skip {
let struct_end_event = XmlEvent::end_element();
let _ret = writer.write(struct_end_event);
writer.write(struct_end_event).map_err(|e| e.to_string())?;
}
Ok(())