Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Deserialize directly to vec #121

Open
wants to merge 13 commits into
base: master
Choose a base branch
from
4 changes: 2 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ version = "0.3.1"
[dependencies]
log = "0.4"
serde = "1.0"
xml-rs = "0.8.0"
thiserror = "1.0"
xml-rs = "=0.8.0"
thiserror = "=1.0.9"

[dev-dependencies]
serde_derive = "1.0"
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# serde-xml-rs

[![Build Status](https://travis-ci.org/RReverser/serde-xml-rs.svg?branch=master)](https://travis-ci.org/RReverser/serde-xml-rs)
[![Build Status](https://travis-ci.org/mward/serde-xml-rs.svg?branch=master)](https://travis-ci.org/mward/serde-xml-rs)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this be included as part of this PR?


xml-rs based deserializer for Serde (compatible with 0.9+)

Expand Down
4 changes: 2 additions & 2 deletions src/de/map.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@ impl<'a, R: 'a + Read> MapAccess<'a, R> {
MapAccess {
attrs: attrs.into_iter(),
next_value: None,
de: de,
inner_value: inner_value,
de,
inner_value,
}
}
}
Expand Down
85 changes: 82 additions & 3 deletions src/de/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,17 +62,21 @@ pub fn from_reader<'de, R: Read, T: de::Deserialize<'de>>(reader: R) -> Result<T
pub struct Deserializer<R: Read> {
depth: usize,
reader: EventReader<R>,
prior: Option<XmlEvent>,
peeked: Option<XmlEvent>,
is_map_value: bool,
stack: Option<Vec<XmlEvent>>,
}

impl<'de, R: Read> Deserializer<R> {
pub fn new(reader: EventReader<R>) -> Self {
Deserializer {
depth: 0,
reader: reader,
reader,
prior: None,
peeked: None,
is_map_value: false,
stack: None,
}
}

Expand All @@ -87,9 +91,38 @@ impl<'de, R: Read> Deserializer<R> {
Self::new(EventReader::new_with_config(reader, config))
}

fn push(&mut self, event: XmlEvent) {
let mut stack = self.stack.take().unwrap_or_else(|| Vec::new());
if let Some(peeked) = self.peeked.take() {
debug!("Pushed {:?}", peeked);
stack.push(peeked);
}

match event {
XmlEvent::StartElement { .. } => {
self.depth -= 1;
}
XmlEvent::EndElement { .. } => {
self.depth += 1;
}
_ => {}
}

debug!("Pushed {:?}", event);
stack.push(event);
self.stack = Some(stack);
}

fn peek(&mut self) -> Result<&XmlEvent> {
if self.peeked.is_none() {
self.peeked = Some(self.inner_next()?);
if let Some(mut stack) = self.stack.take() {
self.peeked = stack.pop();
if stack.len() != 0 {
self.stack = Some(stack);
}
} else {
self.peeked = Some(self.inner_next()?);
}
}
debug_expect!(self.peeked.as_ref(), Some(peeked) => {
debug!("Peeked {:?}", peeked);
Expand All @@ -111,6 +144,12 @@ impl<'de, R: Read> Deserializer<R> {
fn next(&mut self) -> Result<XmlEvent> {
let next = if let Some(peeked) = self.peeked.take() {
peeked
} else if let Some(mut stack) = self.stack.take() {
let peeked = stack.pop().unwrap();
if !stack.is_empty() {
self.stack = Some(stack);
}
peeked
} else {
self.inner_next()?
};
Expand Down Expand Up @@ -325,7 +364,47 @@ impl<'de, 'a, R: Read> de::Deserializer<'de> for &'a mut Deserializer<R> {
}

fn deserialize_seq<V: de::Visitor<'de>>(self, visitor: V) -> Result<V::Value> {
visitor.visit_seq(SeqAccess::new(self, None))
match self.peek()? {
// node without attributes: <container>...
XmlEvent::StartElement { attributes, .. } if attributes.is_empty() => {
match (self.next()?, self.peek()?) {
(next, XmlEvent::StartElement { .. }) => {
let prior = self.prior.take();
self.prior = Some(next);
let result = self.deserialize_seq(visitor);
if let Some(XmlEvent::StartElement { name, .. }) = self.prior.take() {
self.expect_end_element(name)?;
}
self.prior = prior;
result
}
(next, XmlEvent::EndElement { .. }) => {
// empty node <container></container> or <container/>
self.push(next);
let result = visitor.visit_seq(SeqAccess::new(self, Some(0)));

let _start = self.next()?;
let _end = self.next()?;

result
}
(next, _peeked) => { // rewind
if let Some(prior) = self.prior.take() {
self.push(next);
self.push(prior);
visitor.visit_seq(SeqAccess::new(self, None))
} else {
self.push(next);
visitor.visit_seq(SeqAccess::new(self, None))
}
},
}
}
_ => {
// node with attributes: <container name="library">...
visitor.visit_seq(SeqAccess::new(self, None))
}
}
}

fn deserialize_map<V: de::Visitor<'de>>(self, visitor: V) -> Result<V::Value> {
Expand Down
180 changes: 179 additions & 1 deletion tests/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ extern crate simple_logger;

use serde_xml_rs::from_str;

#[derive(Debug, Deserialize, PartialEq)]
#[derive(Debug, Serialize, Deserialize, PartialEq)]
struct Item {
name: String,
source: String,
Expand Down Expand Up @@ -85,6 +85,9 @@ struct Project {

#[serde(rename = "item", default)]
items: Vec<Item>,

#[serde(default)]
sub_projects: Option<Vec<SubProject>>,
}

#[test]
Expand Down Expand Up @@ -114,6 +117,71 @@ fn nested_collection() {
source: "world2.rs".to_string(),
},
],
sub_projects: None,
}
);
}

#[derive(Debug, Serialize, Deserialize, PartialEq)]
struct SubProject {
name: String,

#[serde(default)]
items: Vec<Item>,
}

#[test]
fn nested_collection_with_nested_collection() {
let _ = simple_logger::init();

let s = r##"
<project name="my_project">
<!-- these items do not have a wrapping 'items' tag -->
<item name="hello1" source="world1.rs" />
<item name="hello2" source="world2.rs" />
<sub_projects>
<sub_project name="child1">
<!-- these items are wrapped in a 'items' tag -->
<items>
<item name="foo1" source="bar1.rs" />
<item name="foo2" source="bar2.rs" />
</items>
</sub_project>
</sub_projects>
</project>
"##;

let project: Project = from_str(s).unwrap();

assert_eq!(
project,
Project {
name: "my_project".to_string(),
items: vec![
Item {
name: "hello1".to_string(),
source: "world1.rs".to_string(),
},
Item {
name: "hello2".to_string(),
source: "world2.rs".to_string(),
},
],
sub_projects: Some(vec![
SubProject {
name: "child1".to_string(),
items: vec![
Item {
name: "foo1".to_string(),
source: "bar1.rs".to_string(),
},
Item {
name: "foo2".to_string(),
source: "bar2.rs".to_string(),
},
],
},
]),
}
);
}
Expand Down Expand Up @@ -159,3 +227,113 @@ fn collection_of_enums() {
}
);
}

#[test]
fn nested_collection_with_nested_empty_collection() {
let _ = simple_logger::init();

let s = r##"
<project name="my_project">
<!-- these items do not have a wrapping 'items' tag -->
<item name="hello1" source="world1.rs" />
<item name="hello2" source="world2.rs" />
<sub_projects>
<sub_project name="child1">
<!-- these items are wrapped in a 'items' tag -->
<items></items>
</sub_project>
</sub_projects>
</project>
"##;

let project: Project = from_str(s).unwrap();

assert_eq!(
project,
Project {
name: "my_project".to_string(),
items: vec![
Item {
name: "hello1".to_string(),
source: "world1.rs".to_string(),
},
Item {
name: "hello2".to_string(),
source: "world2.rs".to_string(),
},
],
sub_projects: Some(vec![
SubProject {
name: "child1".to_string(),
items: vec![],
},
]),
}
);
}

#[derive(Debug, Deserialize, PartialEq)]
struct Shelter {
animals: Vec<Animal>,
}

#[derive(Debug, Deserialize, PartialEq)]
struct Animal {
name: String,
foo: String,
}

/// don't require a wrapper struct Vec<?>
#[test]
fn vec_container() {
let _ = simple_logger::init();

let s = r##"
<shelter>
<animals>
<animal name="walter" foo="dog" />
<animal name="max" foo="cat" />
</animals>
</shelter>
"##;

let shelter: Shelter = from_str(s).unwrap();

assert_eq!(
shelter.animals,
vec![
Animal {
name: "walter".to_string(),
foo: "dog".to_string(),
},
Animal {
name: "max".to_string(),
foo: "cat".to_string(),
},
]
);
}

#[test]
fn empty_vec_container() {
let _ = simple_logger::init();

let s = r##"
<shelter>
<animals>
</animals>
</shelter>
"##;

let shelter: Shelter = from_str(s).unwrap();
assert_eq!(shelter.animals, Vec::new());

let s = r##"
<shelter>
<animals/>
</shelter>
"##;

let shelter: Shelter = from_str(s).unwrap();
assert_eq!(shelter.animals, Vec::new());
}