serde-xml-rs-0.5.1/.cargo_vcs_info.json0000644000000001120000000000100133530ustar { "git": { "sha1": "2a13dbd3f6ae77e67d7d329e7fce0b66f2d16d05" } } serde-xml-rs-0.5.1/.github/workflows/lint.yml000064400000000000000000000022220072674642500172650ustar 00000000000000on: push: branches: [ master ] pull_request: branches: [ master ] name: Lint jobs: check: name: Check runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - uses: actions-rs/toolchain@v1 with: profile: minimal toolchain: stable override: true - uses: actions-rs/cargo@v1 with: command: check fmt: name: Format runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - uses: actions-rs/toolchain@v1 with: profile: minimal toolchain: stable override: true - run: rustup component add rustfmt - uses: actions-rs/cargo@v1 with: command: fmt args: --all -- --check clippy: name: Clippy runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - uses: actions-rs/toolchain@v1 with: toolchain: stable components: clippy override: true - uses: actions-rs/clippy-check@v1 with: token: ${{ secrets.GITHUB_TOKEN }} args: --all-features name: Clippy Output serde-xml-rs-0.5.1/.github/workflows/rust.yml000064400000000000000000000004740072674642500173230ustar 00000000000000name: Rust on: push: branches: [ master ] pull_request: branches: [ master ] env: CARGO_TERM_COLOR: always jobs: build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - name: Build run: cargo build --verbose - name: Run tests run: cargo test --verbose serde-xml-rs-0.5.1/.gitignore000064400000000000000000000000220072674642500141630ustar 00000000000000target Cargo.lock serde-xml-rs-0.5.1/.travis.yml000064400000000000000000000003530072674642500143130ustar 00000000000000language: rust rust: - stable - beta - nightly cache: cargo before_cache: - cargo clean -p serde-xml-rs - rm -rf target/ env: global: - RUST_BACKTRACE=1 matrix: fast_finish: true allow_failures: - rust: nightly serde-xml-rs-0.5.1/Cargo.toml0000644000000020050000000000100113540ustar # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2018" name = "serde-xml-rs" version = "0.5.1" authors = ["Ingvar Stepanyan "] description = "xml-rs based deserializer for Serde (compatible with 0.9+)" license = "MIT" repository = "https://github.com/RReverser/serde-xml-rs" [dependencies.log] version = "0.4" [dependencies.serde] version = "1.0" [dependencies.thiserror] version = "1.0" [dependencies.xml-rs] version = "0.8" [dev-dependencies.docmatic] version = "0.1" [dev-dependencies.serde_derive] version = "1.0" [dev-dependencies.simple_logger] version = "1.0" serde-xml-rs-0.5.1/Cargo.toml.orig000064400000000000000000000006440072674642500150740ustar 00000000000000[package] authors = ["Ingvar Stepanyan "] description = "xml-rs based deserializer for Serde (compatible with 0.9+)" license = "MIT" name = "serde-xml-rs" repository = "https://github.com/RReverser/serde-xml-rs" version = "0.5.1" edition = "2018" [dependencies] log = "0.4" serde = "1.0" xml-rs = "0.8" thiserror = "1.0" [dev-dependencies] serde_derive = "1.0" simple_logger = "1.0" docmatic = "0.1" serde-xml-rs-0.5.1/LICENSE000064400000000000000000000020610072674642500132050ustar 00000000000000MIT License Copyright (c) 2017 Ingvar Stepanyan Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. serde-xml-rs-0.5.1/README.md000064400000000000000000000014530072674642500134630ustar 00000000000000# serde-xml-rs [![Build Status](https://travis-ci.org/RReverser/serde-xml-rs.svg?branch=master)](https://travis-ci.org/RReverser/serde-xml-rs) `xml-rs` based deserializer for Serde (compatible with 1.0) ## Example usage ```rust use serde; use serde_derive::{Deserialize, Serialize}; use serde_xml_rs::{from_str, to_string}; #[derive(Debug, Serialize, Deserialize, PartialEq)] struct Item { name: String, source: String, } fn main() { let src = r#"BananaStore"#; let should_be = Item { name: "Banana".to_string(), source: "Store".to_string(), }; let item: Item = from_str(src).unwrap(); assert_eq!(item, should_be); let reserialized_item = to_string(&item).unwrap(); assert_eq!(src, reserialized_item); } ``` serde-xml-rs-0.5.1/rustfmt.toml000064400000000000000000000000000072674642500145700ustar 00000000000000serde-xml-rs-0.5.1/src/de/buffer.rs000064400000000000000000000123120072674642500151760ustar 00000000000000use crate::debug_expect; use crate::error::Result; use std::{collections::VecDeque, io::Read}; use xml::reader::{EventReader, XmlEvent}; /// Retrieve XML events from an underlying reader. pub trait BufferedXmlReader { /// Get and "consume" the next event. fn next(&mut self) -> Result; /// Get the next event without consuming. fn peek(&mut self) -> Result<&XmlEvent>; /// Spawn a child buffer whose cursor starts at the same position as this buffer. fn child_buffer<'a>(&'a mut self) -> ChildXmlBuffer<'a, R>; } pub struct RootXmlBuffer { reader: EventReader, buffer: VecDeque, } impl RootXmlBuffer { pub fn new(reader: EventReader) -> Self { RootXmlBuffer { reader, buffer: VecDeque::new(), } } } impl BufferedXmlReader for RootXmlBuffer { /// Consumed XML events in the root buffer are moved to the caller fn next(&mut self) -> Result { loop { match self.buffer.pop_front() { Some(CachedXmlEvent::Unused(ev)) => break Ok(ev), Some(CachedXmlEvent::Used) => continue, None => break next_significant_event(&mut self.reader), } } } fn peek(&mut self) -> Result<&XmlEvent> { get_from_buffer_or_reader(&mut self.buffer, &mut self.reader, &mut 0) } fn child_buffer<'root>(&'root mut self) -> ChildXmlBuffer<'root, R> { let RootXmlBuffer { reader, buffer } = self; ChildXmlBuffer { reader, buffer, cursor: 0, } } } pub struct ChildXmlBuffer<'parent, R: Read> { reader: &'parent mut EventReader, buffer: &'parent mut VecDeque, cursor: usize, } impl<'parent, R: Read> ChildXmlBuffer<'parent, R> { /// Advance the child buffer without marking an event as "used" pub fn skip(&mut self) { debug_assert!( self.cursor < self.buffer.len(), ".skip() only should be called after .peek()" ); self.cursor += 1; } } impl<'parent, R: Read> BufferedXmlReader for ChildXmlBuffer<'parent, R> { /// Consumed XML events in a child buffer are marked as "used" fn next(&mut self) -> Result { loop { match self.buffer.get_mut(self.cursor) { Some(entry @ CachedXmlEvent::Unused(_)) => { let taken = if self.cursor == 0 { self.buffer.pop_front().unwrap() } else { std::mem::replace(entry, CachedXmlEvent::Used) }; return debug_expect!(taken, CachedXmlEvent::Unused(ev) => Ok(ev)); } Some(CachedXmlEvent::Used) => { debug_assert!( self.cursor != 0, "Event buffer should not start with 'used' slot (should have been popped)" ); self.cursor += 1; continue; } None => { debug_assert_eq!(self.buffer.len(), self.cursor); // Skip creation of buffer entry when consuming event straight away return next_significant_event(&mut self.reader); } } } } fn peek(&mut self) -> Result<&XmlEvent> { get_from_buffer_or_reader(self.buffer, self.reader, &mut self.cursor) } fn child_buffer<'a>(&'a mut self) -> ChildXmlBuffer<'a, R> { let ChildXmlBuffer { reader, buffer, cursor, } = self; ChildXmlBuffer { reader, buffer, cursor: *cursor, } } } #[derive(Debug)] enum CachedXmlEvent { Unused(XmlEvent), Used, } fn get_from_buffer_or_reader<'buf>( buffer: &'buf mut VecDeque, reader: &mut EventReader, index: &mut usize, ) -> Result<&'buf XmlEvent> { // We should only be attempting to get an event already in the buffer, or the next event to place in the buffer debug_assert!(*index <= buffer.len()); loop { match buffer.get_mut(*index) { Some(CachedXmlEvent::Unused(_)) => break, Some(CachedXmlEvent::Used) => { *index += 1; } None => { let next = next_significant_event(reader)?; buffer.push_back(CachedXmlEvent::Unused(next)); } } } // Returning of borrowed data must be done after of loop/match due to current limitation of borrow checker debug_expect!(buffer.get_mut(*index), Some(CachedXmlEvent::Unused(event)) => Ok(event)) } /// Reads the next XML event from the underlying reader, skipping events we're not interested in. fn next_significant_event(reader: &mut EventReader) -> Result { loop { match reader.next()? { XmlEvent::StartDocument { .. } | XmlEvent::ProcessingInstruction { .. } | XmlEvent::Whitespace { .. } | XmlEvent::Comment(_) => { /* skip */ } other => return Ok(other), } } } serde-xml-rs-0.5.1/src/de/map.rs000064400000000000000000000117160072674642500145110ustar 00000000000000use std::io::Read; use serde::de::{self, IntoDeserializer, Unexpected}; use serde::forward_to_deserialize_any; use xml::attribute::OwnedAttribute; use xml::reader::XmlEvent; use crate::error::{Error, Result}; use crate::Deserializer; use super::buffer::BufferedXmlReader; pub struct MapAccess<'a, R: Read, B: BufferedXmlReader> { attrs: ::std::vec::IntoIter, /// Cache of attribute value, populated when visitor calls `next_key_seed`; should be read & emptied straight after /// by visitor call to `next_value_seed` next_attr_value: Option, de: &'a mut Deserializer, /// Whether this `MapAccess` is to deserialize all inner contents of an outer element. inner_value: bool, } impl<'a, R: 'a + Read, B: BufferedXmlReader> MapAccess<'a, R, B> { pub fn new( de: &'a mut Deserializer, attrs: Vec, inner_value: bool, ) -> Self { MapAccess { attrs: attrs.into_iter(), next_attr_value: None, de: de, inner_value: inner_value, } } } impl<'de, 'a, R: 'a + Read, B: BufferedXmlReader> de::MapAccess<'de> for MapAccess<'a, R, B> { type Error = Error; fn next_key_seed>(&mut self, seed: K) -> Result> { debug_assert_eq!(self.next_attr_value, None); match self.attrs.next() { // Read all attributes first Some(OwnedAttribute { name, value }) => { self.next_attr_value = Some(value); seed.deserialize(name.local_name.into_deserializer()) .map(Some) } None => match *self.de.peek()? { XmlEvent::StartElement { ref name, .. } => seed .deserialize( if !self.inner_value { name.local_name.as_str() } else { "$value" } .into_deserializer(), ) .map(Some), XmlEvent::Characters(_) => seed.deserialize("$value".into_deserializer()).map(Some), // Any other event: assume end of map values (actual check for `EndElement` done by the originating // `Deserializer`) _ => Ok(None), }, } } fn next_value_seed>(&mut self, seed: V) -> Result { match self.next_attr_value.take() { Some(value) => seed.deserialize(AttrValueDeserializer(value)), None => { if !self.inner_value { if let XmlEvent::StartElement { .. } = *self.de.peek()? { self.de.set_map_value(); } } let result = seed.deserialize(&mut *self.de)?; Ok(result) } } } fn size_hint(&self) -> Option { self.attrs.size_hint().1 } } struct AttrValueDeserializer(String); macro_rules! deserialize_type_attr { ($deserialize:ident => $visit:ident) => { fn $deserialize>(self, visitor: V) -> Result { visitor.$visit(self.0.parse()?) } }; } impl<'de> de::Deserializer<'de> for AttrValueDeserializer { type Error = Error; fn deserialize_any>(self, visitor: V) -> Result { visitor.visit_string(self.0) } deserialize_type_attr!(deserialize_i8 => visit_i8); deserialize_type_attr!(deserialize_i16 => visit_i16); deserialize_type_attr!(deserialize_i32 => visit_i32); deserialize_type_attr!(deserialize_i64 => visit_i64); deserialize_type_attr!(deserialize_u8 => visit_u8); deserialize_type_attr!(deserialize_u16 => visit_u16); deserialize_type_attr!(deserialize_u32 => visit_u32); deserialize_type_attr!(deserialize_u64 => visit_u64); deserialize_type_attr!(deserialize_f32 => visit_f32); deserialize_type_attr!(deserialize_f64 => visit_f64); fn deserialize_enum>( self, _name: &str, _variants: &'static [&'static str], visitor: V, ) -> Result { visitor.visit_enum(self.0.into_deserializer()) } fn deserialize_option>(self, visitor: V) -> Result { visitor.visit_some(self) } fn deserialize_bool>(self, visitor: V) -> Result { match self.0.as_str() { "true" | "1" => visitor.visit_bool(true), "false" | "0" => visitor.visit_bool(false), _ => Err(de::Error::invalid_value( Unexpected::Str(&self.0), &"a boolean", )), } } forward_to_deserialize_any! { char str string unit seq bytes map unit_struct newtype_struct tuple_struct struct identifier tuple ignored_any byte_buf } } serde-xml-rs-0.5.1/src/de/mod.rs000064400000000000000000000333140072674642500145110ustar 00000000000000use std::{io::Read, marker::PhantomData}; use log::debug; use serde::de::{self, Unexpected}; use serde::forward_to_deserialize_any; use xml::name::OwnedName; use xml::reader::{EventReader, ParserConfig, XmlEvent}; use self::buffer::{BufferedXmlReader, ChildXmlBuffer, RootXmlBuffer}; use self::map::MapAccess; use self::seq::SeqAccess; use self::var::EnumAccess; use crate::error::{Error, Result}; use crate::{debug_expect, expect}; mod buffer; mod map; mod seq; mod var; /// A convenience method for deserialize some object from a string. /// /// ```rust /// # #[macro_use] /// # extern crate serde_derive; /// # extern crate serde; /// # extern crate serde_xml_rs; /// # use serde_xml_rs::from_str; /// #[derive(Debug, Deserialize, PartialEq)] /// struct Item { /// name: String, /// source: String, /// } /// # fn main() { /// let s = r##""##; /// let item: Item = from_str(s).unwrap(); /// assert_eq!(item, Item { name: "hello".to_string(),source: "world.rs".to_string()}); /// # } /// ``` pub fn from_str<'de, T: de::Deserialize<'de>>(s: &str) -> Result { from_reader(s.as_bytes()) } /// A convenience method for deserialize some object from a reader. /// /// ```rust /// # #[macro_use] /// # extern crate serde_derive; /// # extern crate serde; /// # extern crate serde_xml_rs; /// # use serde_xml_rs::from_reader; /// #[derive(Debug, Deserialize, PartialEq)] /// struct Item { /// name: String, /// source: String, /// } /// # fn main() { /// let s = r##""##; /// let item: Item = from_reader(s.as_bytes()).unwrap(); /// assert_eq!(item, Item { name: "hello".to_string(),source: "world.rs".to_string()}); /// # } /// ``` pub fn from_reader<'de, R: Read, T: de::Deserialize<'de>>(reader: R) -> Result { T::deserialize(&mut Deserializer::new_from_reader(reader)) } type RootDeserializer = Deserializer>; type ChildDeserializer<'parent, R> = Deserializer>; pub struct Deserializer< R: Read, // Kept as type param to avoid type signature breaking-change B: BufferedXmlReader = RootXmlBuffer, > { /// XML document nested element depth depth: usize, buffered_reader: B, is_map_value: bool, non_contiguous_seq_elements: bool, marker: PhantomData, } impl<'de, R: Read> RootDeserializer { pub fn new(reader: EventReader) -> Self { let buffered_reader = RootXmlBuffer::new(reader); Deserializer { buffered_reader, depth: 0, is_map_value: false, non_contiguous_seq_elements: false, marker: PhantomData, } } pub fn new_from_reader(reader: R) -> Self { let config = ParserConfig::new() .trim_whitespace(true) .whitespace_to_characters(true) .cdata_to_characters(true) .ignore_comments(true) .coalesce_characters(true); Self::new(EventReader::new_with_config(reader, config)) } /// Configures whether the deserializer should search all sibling elements when building a /// sequence. Not required if all XML elements for sequences are adjacent. Disabled by /// default. Enabling this option may incur additional memory usage. /// /// ```rust /// # #[macro_use] /// # extern crate serde_derive; /// # extern crate serde; /// # extern crate serde_xml_rs; /// # use serde_xml_rs::from_reader; /// # use serde::Deserialize; /// #[derive(Debug, Deserialize, PartialEq)] /// struct Foo { /// bar: Vec, /// baz: String, /// } /// # fn main() { /// let s = r##" /// /// 1 /// 2 /// Hello, world /// 3 /// 4 /// /// "##; /// let mut de = serde_xml_rs::Deserializer::new_from_reader(s.as_bytes()) /// .non_contiguous_seq_elements(true); /// let foo = Foo::deserialize(&mut de).unwrap(); /// assert_eq!(foo, Foo { bar: vec![1, 2, 3, 4], baz: "Hello, world".to_string()}); /// # } /// ``` pub fn non_contiguous_seq_elements(mut self, set: bool) -> Self { self.non_contiguous_seq_elements = set; self } } impl<'de, R: Read, B: BufferedXmlReader> Deserializer { fn child<'a>(&'a mut self) -> Deserializer> { let Deserializer { buffered_reader, depth, is_map_value, non_contiguous_seq_elements, .. } = self; Deserializer { buffered_reader: buffered_reader.child_buffer(), depth: *depth, is_map_value: *is_map_value, non_contiguous_seq_elements: *non_contiguous_seq_elements, marker: PhantomData, } } /// Gets the next XML event without advancing the cursor. fn peek(&mut self) -> Result<&XmlEvent> { let peeked = self.buffered_reader.peek()?; debug!("Peeked {:?}", peeked); Ok(peeked) } /// Gets the XML event at the cursor and advances the cursor. fn next(&mut self) -> Result { let next = self.buffered_reader.next()?; match next { XmlEvent::StartElement { .. } => { self.depth += 1; } XmlEvent::EndElement { .. } => { self.depth -= 1; } _ => {} } debug!("Fetched {:?}", next); Ok(next) } fn set_map_value(&mut self) { self.is_map_value = true; } pub fn unset_map_value(&mut self) -> bool { ::std::mem::replace(&mut self.is_map_value, false) } /// If `self.is_map_value`: Performs the read operations specified by `f` on the inner content of an XML element. /// `f` is expected to consume the entire inner contents of the element. The cursor will be moved to the end of the /// element. /// If `!self.is_map_value`: `f` will be performed without additional checks/advances for an outer XML element. fn read_inner_value, T, F: FnOnce(&mut Self) -> Result>( &mut self, f: F, ) -> Result { if self.unset_map_value() { debug_expect!(self.next(), Ok(XmlEvent::StartElement { name, .. }) => { let result = f(self)?; self.expect_end_element(name)?; Ok(result) }) } else { f(self) } } fn expect_end_element(&mut self, start_name: OwnedName) -> Result<()> { expect!(self.next()?, XmlEvent::EndElement { name, .. } => { if name == start_name { Ok(()) } else { Err(Error::Custom { field: format!( "End tag didn't match the start tag <{}>", name.local_name, start_name.local_name ) }) } }) } fn prepare_parse_type>(&mut self) -> Result { if let XmlEvent::StartElement { .. } = *self.peek()? { self.set_map_value() } self.read_inner_value::(|this| { if let XmlEvent::EndElement { .. } = *this.peek()? { return Err(Error::UnexpectedToken { token: "EndElement".into(), found: "Characters".into(), }); } expect!(this.next()?, XmlEvent::Characters(s) => { return Ok(s) }) }) } } macro_rules! deserialize_type { ($deserialize:ident => $visit:ident) => { fn $deserialize>(self, visitor: V) -> Result { let value = self.prepare_parse_type::()?.parse()?; visitor.$visit(value) } }; } impl<'de, 'a, R: Read, B: BufferedXmlReader> de::Deserializer<'de> for &'a mut Deserializer { type Error = Error; forward_to_deserialize_any! { identifier } fn deserialize_struct>( self, _name: &'static str, fields: &'static [&'static str], visitor: V, ) -> Result { self.unset_map_value(); expect!(self.next()?, XmlEvent::StartElement { name, attributes, .. } => { let map_value = visitor.visit_map(MapAccess::new( self, attributes, fields.contains(&"$value") ))?; self.expect_end_element(name)?; Ok(map_value) }) } deserialize_type!(deserialize_i8 => visit_i8); deserialize_type!(deserialize_i16 => visit_i16); deserialize_type!(deserialize_i32 => visit_i32); deserialize_type!(deserialize_i64 => visit_i64); deserialize_type!(deserialize_u8 => visit_u8); deserialize_type!(deserialize_u16 => visit_u16); deserialize_type!(deserialize_u32 => visit_u32); deserialize_type!(deserialize_u64 => visit_u64); deserialize_type!(deserialize_f32 => visit_f32); deserialize_type!(deserialize_f64 => visit_f64); fn deserialize_bool>(self, visitor: V) -> Result { if let XmlEvent::StartElement { .. } = *self.peek()? { self.set_map_value() } self.read_inner_value::(|this| { if let XmlEvent::EndElement { .. } = *this.peek()? { return visitor.visit_bool(false); } expect!(this.next()?, XmlEvent::Characters(s) => { match s.as_str() { "true" | "1" => visitor.visit_bool(true), "false" | "0" => visitor.visit_bool(false), _ => Err(de::Error::invalid_value(Unexpected::Str(&s), &"a boolean")), } }) }) } fn deserialize_char>(self, visitor: V) -> Result { self.deserialize_string(visitor) } fn deserialize_str>(self, visitor: V) -> Result { self.deserialize_string(visitor) } fn deserialize_bytes>(self, visitor: V) -> Result { self.deserialize_string(visitor) } fn deserialize_byte_buf>(self, visitor: V) -> Result { self.deserialize_string(visitor) } fn deserialize_unit>(self, visitor: V) -> Result { if let XmlEvent::StartElement { .. } = *self.peek()? { self.set_map_value() } self.read_inner_value::( |this| expect!(this.peek()?, &XmlEvent::EndElement { .. } => visitor.visit_unit()), ) } fn deserialize_unit_struct>( self, _name: &'static str, visitor: V, ) -> Result { self.deserialize_unit(visitor) } fn deserialize_newtype_struct>( self, _name: &'static str, visitor: V, ) -> Result { visitor.visit_newtype_struct(self) } fn deserialize_tuple_struct>( self, _name: &'static str, len: usize, visitor: V, ) -> Result { self.deserialize_tuple(len, visitor) } fn deserialize_tuple>(self, len: usize, visitor: V) -> Result { let child_deserializer = self.child(); visitor.visit_seq(SeqAccess::new(child_deserializer, Some(len))) } fn deserialize_enum>( self, _name: &'static str, _variants: &'static [&'static str], visitor: V, ) -> Result { self.read_inner_value::(|this| visitor.visit_enum(EnumAccess::new(this))) } fn deserialize_string>(self, visitor: V) -> Result { if let XmlEvent::StartElement { .. } = *self.peek()? { self.set_map_value() } self.read_inner_value::(|this| { if let XmlEvent::EndElement { .. } = *this.peek()? { return visitor.visit_str(""); } expect!(this.next()?, XmlEvent::Characters(s) => { visitor.visit_string(s) }) }) } fn deserialize_seq>(self, visitor: V) -> Result { let child_deserializer = self.child(); visitor.visit_seq(SeqAccess::new(child_deserializer, None)) } fn deserialize_map>(self, visitor: V) -> Result { self.unset_map_value(); expect!(self.next()?, XmlEvent::StartElement { name, attributes, .. } => { let map_value = visitor.visit_map(MapAccess::new(self, attributes, false))?; self.expect_end_element(name)?; Ok(map_value) }) } fn deserialize_option>(self, visitor: V) -> Result { match *self.peek()? { XmlEvent::EndElement { .. } => visitor.visit_none(), _ => visitor.visit_some(self), } } fn deserialize_ignored_any>(self, visitor: V) -> Result { self.unset_map_value(); let depth = self.depth; loop { self.next()?; if self.depth == depth { break; } } visitor.visit_unit() } fn deserialize_any>(self, visitor: V) -> Result { match *self.peek()? { XmlEvent::StartElement { .. } => self.deserialize_map(visitor), XmlEvent::EndElement { .. } => self.deserialize_unit(visitor), _ => self.deserialize_string(visitor), } } } serde-xml-rs-0.5.1/src/de/seq.rs000064400000000000000000000072420072674642500145230ustar 00000000000000use std::io::Read; use serde::de; use xml::reader::XmlEvent; use crate::de::ChildDeserializer; use crate::debug_expect; use crate::error::{Error, Result}; pub struct SeqAccess<'a, R: Read> { de: ChildDeserializer<'a, R>, max_size: Option, seq_type: SeqType, } pub enum SeqType { /// Sequence is of elements with the same name. ByElementName { expected_name: String, search_non_contiguous: bool, }, /// Sequence is of all elements/text at current depth. AllMembers, } impl<'a, R: 'a + Read> SeqAccess<'a, R> { pub fn new(mut de: ChildDeserializer<'a, R>, max_size: Option) -> Self { let seq_type = if de.unset_map_value() { debug_expect!(de.peek(), Ok(&XmlEvent::StartElement { ref name, .. }) => { SeqType::ByElementName { expected_name: name.local_name.clone(), search_non_contiguous: de.non_contiguous_seq_elements } }) } else { SeqType::AllMembers }; SeqAccess { de, max_size, seq_type, } } } impl<'de, 'a, R: 'a + Read> de::SeqAccess<'de> for SeqAccess<'a, R> { type Error = Error; fn next_element_seed>( &mut self, seed: T, ) -> Result> { match self.max_size.as_mut() { Some(&mut 0) => { return Ok(None); } Some(max_size) => { *max_size -= 1; } None => {} } match &self.seq_type { SeqType::ByElementName { expected_name, search_non_contiguous, } => { let mut local_depth = 0; loop { let next_element = self.de.peek()?; match next_element { XmlEvent::StartElement { name, .. } if &name.local_name == expected_name && local_depth == 0 => { self.de.set_map_value(); return seed.deserialize(&mut self.de).map(Some); } XmlEvent::StartElement { .. } => { if *search_non_contiguous { self.de.buffered_reader.skip(); local_depth += 1; } else { return Ok(None); } } XmlEvent::EndElement { .. } => { if local_depth == 0 { return Ok(None); } else { local_depth -= 1; self.de.buffered_reader.skip(); } } XmlEvent::EndDocument => { return Ok(None); } _ => { self.de.buffered_reader.skip(); } } } } SeqType::AllMembers => { let next_element = self.de.peek()?; match next_element { XmlEvent::EndElement { .. } | XmlEvent::EndDocument => return Ok(None), _ => { return seed.deserialize(&mut self.de).map(Some); } } } } } fn size_hint(&self) -> Option { self.max_size } } serde-xml-rs-0.5.1/src/de/var.rs000064400000000000000000000051360072674642500145230ustar 00000000000000use std::io::Read; use serde::de::{self, Deserializer as SerdeDeserializer, IntoDeserializer}; use xml::name::OwnedName; use xml::reader::XmlEvent; use crate::de::Deserializer; use crate::error::{Error, Result}; use crate::expect; use super::buffer::BufferedXmlReader; pub struct EnumAccess<'a, R: Read, B: BufferedXmlReader> { de: &'a mut Deserializer, } impl<'a, R: 'a + Read, B: BufferedXmlReader> EnumAccess<'a, R, B> { pub fn new(de: &'a mut Deserializer) -> Self { EnumAccess { de: de } } } impl<'de, 'a, R: 'a + Read, B: BufferedXmlReader> de::EnumAccess<'de> for EnumAccess<'a, R, B> { type Error = Error; type Variant = VariantAccess<'a, R, B>; fn variant_seed>( self, seed: V, ) -> Result<(V::Value, VariantAccess<'a, R, B>)> { let name = expect!( self.de.peek()?, &XmlEvent::Characters(ref name) | &XmlEvent::StartElement { name: OwnedName { local_name: ref name, .. }, .. } => { seed.deserialize(name.as_str().into_deserializer()) } )?; self.de.set_map_value(); Ok((name, VariantAccess::new(self.de))) } } pub struct VariantAccess<'a, R: Read, B: BufferedXmlReader> { de: &'a mut Deserializer, } impl<'a, R: 'a + Read, B: BufferedXmlReader> VariantAccess<'a, R, B> { pub fn new(de: &'a mut Deserializer) -> Self { VariantAccess { de: de } } } impl<'de, 'a, R: 'a + Read, B: BufferedXmlReader> de::VariantAccess<'de> for VariantAccess<'a, R, B> { type Error = Error; fn unit_variant(self) -> Result<()> { self.de.unset_map_value(); match self.de.next()? { XmlEvent::StartElement { name, attributes, .. } => { if attributes.is_empty() { self.de.expect_end_element(name) } else { Err(de::Error::invalid_length(attributes.len(), &"0")) } } XmlEvent::Characters(_) => Ok(()), _ => unreachable!(), } } fn newtype_variant_seed>(self, seed: T) -> Result { seed.deserialize(&mut *self.de) } fn tuple_variant>(self, len: usize, visitor: V) -> Result { self.de.deserialize_tuple(len, visitor) } fn struct_variant>( self, _fields: &'static [&'static str], visitor: V, ) -> Result { self.de.deserialize_map(visitor) } } serde-xml-rs-0.5.1/src/error.rs000064400000000000000000000047320072674642500144750ustar 00000000000000use serde::de::Error as DeError; use serde::ser::Error as SerError; use std::fmt::Display; use thiserror::Error; #[derive(Debug, Error)] pub enum Error { #[error("Expected token {token}, found {found}")] UnexpectedToken { token: String, found: String }, #[error("custom: {field}")] Custom { field: String }, #[error("unsupported operation: '{operation}'")] UnsupportedOperation { operation: String }, #[error("IO error: {source}")] Io { #[from] source: ::std::io::Error, }, #[error("FromUtf8Error: {source}")] FromUtf8Error { #[from] source: ::std::string::FromUtf8Error, }, #[error("ParseIntError: {source}")] ParseIntError { #[from] source: ::std::num::ParseIntError, }, #[error("ParseFloatError: {source}")] ParseFloatError { #[from] source: ::std::num::ParseFloatError, }, #[error("ParseBoolError: {source}")] ParseBoolError { #[from] source: ::std::str::ParseBoolError, }, #[error("Syntax: {source}")] Syntax { #[from] source: ::xml::reader::Error, }, } pub type Result = std::result::Result; #[macro_export] macro_rules! expect { ($actual: expr, $($expected: pat)|+ => $if_ok: expr) => { match $actual { $($expected)|+ => $if_ok, actual => Err($crate::Error::UnexpectedToken { token: stringify!($($expected)|+).to_string(), found: format!("{:?}",actual) }) as Result<_> } } } #[cfg(debug_assertions)] #[macro_export] macro_rules! debug_expect { ($actual: expr, $($expected: pat)|+ => $if_ok: expr) => { match $actual { $($expected)|+ => $if_ok, actual => panic!( "Internal error: Expected token {}, found {:?}", stringify!($($expected)|+), actual ) } } } #[cfg(not(debug_assertions))] #[macro_export] macro_rules! debug_expect { ($actual: expr, $($expected: pat)|+ => $if_ok: expr) => { match $actual { $($expected)|+ => $if_ok, _ => unreachable!() } } } impl DeError for Error { fn custom(msg: T) -> Self { Error::Custom { field: msg.to_string(), } } } impl SerError for Error { fn custom(msg: T) -> Self { Error::Custom { field: msg.to_string(), } } } serde-xml-rs-0.5.1/src/lib.rs000064400000000000000000000137430072674642500141140ustar 00000000000000//! # Serde XML //! //! XML is a flexible markup language that is still used for sharing data between applications or //! for writing configuration files. //! //! Serde XML provides a way to convert between text and strongly-typed Rust data structures. //! //! ## Caveats //! //! The Serde framework was mainly designed with formats such as JSON or YAML in mind. //! As opposed to XML, these formats have the advantage of a stricter syntax which makes it //! possible to know what type a field is without relying on an accompanying schema, //! and disallows repeating the same tag multiple times in the same object. //! //! For example, encoding the following document in YAML is not trivial. //! //! ```xml //! //!
A header
//!
First section
//!
Second section
//! A sidenote //!
Third section
//! Another sidenote //!
Fourth section
//!
The footer
//!
//! ``` //! //! One possibility is the following YAML document. //! //! ```yaml //! - header: A header //! - section: First section //! - section: Second section //! - sidenote: A sidenote //! - section: Third section //! - sidenote: Another sidenote //! - section: Fourth section //! - footer: The footer //! ``` //! //! Other notable differences: //! - XML requires a named root node. //! - XML has a namespace system. //! - XML distinguishes between attributes, child tags and contents. //! - In XML, the order of nodes is sometimes important. //! //! ## Basic example //! //! ```rust //! use serde; //! use serde_derive::{Deserialize, Serialize}; //! use serde_xml_rs::{from_str, to_string}; //! //! #[derive(Debug, Serialize, Deserialize, PartialEq)] //! struct Item { //! name: String, //! source: String, //! } //! //! fn main() { //! let src = r#"BananaStore"#; //! let should_be = Item { //! name: "Banana".to_string(), //! source: "Store".to_string(), //! }; //! //! let item: Item = from_str(src).unwrap(); //! assert_eq!(item, should_be); //! //! let reserialized_item = to_string(&item).unwrap(); //! assert_eq!(src, reserialized_item); //! } //! ``` //! //! ## Tag contents //! //! ```rust //! # use serde; //! # use serde_derive::{Deserialize, Serialize}; //! # use serde_xml_rs::{from_str, to_string}; //! //! #[derive(Debug, Serialize, Deserialize, PartialEq)] //! struct Document { //! content: Content //! } //! //! #[derive(Debug, Serialize, Deserialize, PartialEq)] //! struct Content { //! #[serde(rename = "$value")] //! value: String //! } //! //! fn main() { //! let src = r#"Lorem ipsum"#; //! let document: Document = from_str(src).unwrap(); //! assert_eq!(document.content.value, "Lorem ipsum"); //! } //! ``` //! //! ## Repeated tags //! //! ```rust //! # use serde; //! # use serde_derive::{Deserialize, Serialize}; //! # use serde_xml_rs::{from_str, to_string}; //! //! #[derive(Debug, Serialize, Deserialize, PartialEq)] //! struct PlateAppearance { //! #[serde(rename = "$value")] //! events: Vec //! } //! //! #[derive(Debug, Serialize, Deserialize, PartialEq)] //! #[serde(rename_all = "kebab-case")] //! enum Event { //! Pitch(Pitch), //! Runner(Runner), //! } //! //! #[derive(Debug, Serialize, Deserialize, PartialEq)] //! struct Pitch { //! speed: u32, //! r#type: PitchType, //! outcome: PitchOutcome, //! } //! //! #[derive(Debug, Serialize, Deserialize, PartialEq)] //! enum PitchType { FourSeam, TwoSeam, Changeup, Cutter, Curve, Slider, Knuckle, Pitchout } //! //! #[derive(Debug, Serialize, Deserialize, PartialEq)] //! enum PitchOutcome { Ball, Strike, Hit } //! //! #[derive(Debug, Serialize, Deserialize, PartialEq)] //! struct Runner { //! from: Base, to: Option, outcome: RunnerOutcome, //! } //! //! #[derive(Debug, Serialize, Deserialize, PartialEq)] //! enum Base { First, Second, Third, Home } //! #[derive(Debug, Serialize, Deserialize, PartialEq)] //! enum RunnerOutcome { Steal, Caught, PickOff } //! //! fn main() { //! let document = r#" //! //! //! //! //! //! //! //! "#; //! let plate_appearance: PlateAppearance = from_str(document).unwrap(); //! assert_eq!(plate_appearance.events[0], Event::Pitch(Pitch { speed: 95, r#type: PitchType::FourSeam, outcome: PitchOutcome::Ball })); //! } //! ``` //! //! ## Custom EventReader //! //! ```rust //! use serde::Deserialize; //! use serde_derive::{Deserialize, Serialize}; //! use serde_xml_rs::{from_str, to_string, de::Deserializer}; //! use xml::reader::{EventReader, ParserConfig}; //! //! #[derive(Debug, Serialize, Deserialize, PartialEq)] //! struct Item { //! name: String, //! source: String, //! } //! //! fn main() { //! let src = r#" Banana Store"#; //! let should_be = Item { //! name: " Banana ".to_string(), //! source: "Store".to_string(), //! }; //! //! let config = ParserConfig::new() //! .trim_whitespace(false) //! .whitespace_to_characters(true); //! let event_reader = EventReader::new_with_config(src.as_bytes(), config); //! let item = Item::deserialize(&mut Deserializer::new(event_reader)).unwrap(); //! assert_eq!(item, should_be); //! } //! ``` //! pub mod de; mod error; pub mod ser; pub use crate::de::{from_reader, from_str, Deserializer}; pub use crate::error::Error; pub use crate::ser::{to_string, to_writer, Serializer}; pub use xml::reader::{EventReader, ParserConfig}; serde-xml-rs-0.5.1/src/ser/mod.rs000064400000000000000000000250120072674642500147060ustar 00000000000000use std::fmt::Display; use std::io::Write; use serde::ser::{self, Impossible, Serialize}; use self::var::{Map, Struct}; use crate::error::{Error, Result}; mod var; /// A convenience method for serializing some object to a buffer. /// /// # Examples /// /// ```rust /// # #[macro_use] /// # extern crate serde_derive; /// # extern crate serde; /// # extern crate serde_xml_rs; /// # use serde_xml_rs::to_writer; /// #[derive(Serialize)] /// struct Person { /// name: String, /// age: u32, /// } /// /// # fn main() { /// let mut buffer = Vec::new(); /// let joe = Person {name: "Joe".to_string(), age: 42}; /// /// to_writer(&mut buffer, &joe).unwrap(); /// /// let serialized = String::from_utf8(buffer).unwrap(); /// println!("{}", serialized); /// # } /// ``` pub fn to_writer(writer: W, value: &S) -> Result<()> { let mut ser = Serializer::new(writer); value.serialize(&mut ser) } /// A convenience method for serializing some object to a string. /// /// # Examples /// /// ```rust /// # #[macro_use] /// # extern crate serde_derive; /// # extern crate serde; /// # extern crate serde_xml_rs; /// # use serde_xml_rs::to_string; /// #[derive(Serialize)] /// struct Person { /// name: String, /// age: u32, /// } /// /// # fn main() { /// /// let joe = Person {name: "Joe".to_string(), age: 42}; /// let serialized = to_string(&joe).unwrap(); /// println!("{}", serialized); /// # } /// ``` pub fn to_string(value: &S) -> Result { // Create a buffer and serialize our nodes into it let mut writer = Vec::with_capacity(128); to_writer(&mut writer, value)?; // We then check that the serialized string is the same as what we expect let string = String::from_utf8(writer)?; Ok(string) } /// An XML `Serializer`. pub struct Serializer where W: Write, { writer: W, } impl Serializer where W: Write, { pub fn new(writer: W) -> Self { Self { writer: writer } } fn write_primitive(&mut self, primitive: P) -> Result<()> { write!(self.writer, "{}", primitive)?; Ok(()) } fn write_wrapped(&mut self, tag: &str, value: S) -> Result<()> { write!(self.writer, "<{}>", tag)?; value.serialize(&mut *self)?; write!(self.writer, "", tag)?; Ok(()) } } #[allow(unused_variables)] impl<'w, W> ser::Serializer for &'w mut Serializer where W: Write, { type Ok = (); type Error = Error; type SerializeSeq = Impossible; type SerializeTuple = Impossible; type SerializeTupleStruct = Impossible; type SerializeTupleVariant = Impossible; type SerializeMap = Map<'w, W>; type SerializeStruct = Struct<'w, W>; type SerializeStructVariant = Impossible; fn serialize_bool(self, v: bool) -> Result { if v { write!(self.writer, "true")?; } else { write!(self.writer, "false")?; } Ok(()) } fn serialize_i8(self, v: i8) -> Result { self.write_primitive(v) } fn serialize_i16(self, v: i16) -> Result { self.write_primitive(v) } fn serialize_i32(self, v: i32) -> Result { self.write_primitive(v) } fn serialize_i64(self, v: i64) -> Result { self.write_primitive(v) } fn serialize_u8(self, v: u8) -> Result { self.write_primitive(v) } fn serialize_u16(self, v: u16) -> Result { self.write_primitive(v) } fn serialize_u32(self, v: u32) -> Result { self.write_primitive(v) } fn serialize_u64(self, v: u64) -> Result { self.write_primitive(v) } fn serialize_f32(self, v: f32) -> Result { self.write_primitive(v) } fn serialize_f64(self, v: f64) -> Result { self.write_primitive(v) } fn serialize_char(self, v: char) -> Result { self.write_primitive(v) } fn serialize_str(self, value: &str) -> Result { self.write_primitive(value) } fn serialize_bytes(self, value: &[u8]) -> Result { // TODO: I imagine you'd want to use base64 here. // Not sure how to roundtrip effectively though... Err(Error::UnsupportedOperation { operation: "serialize_bytes".to_string(), }) } fn serialize_none(self) -> Result { Ok(()) } fn serialize_some(self, value: &T) -> Result { value.serialize(self) } fn serialize_unit(self) -> Result { self.serialize_none() } fn serialize_unit_struct(self, name: &'static str) -> Result { self.write_wrapped(name, ()) } fn serialize_unit_variant( self, name: &'static str, variant_index: u32, variant: &'static str, ) -> Result { Err(Error::UnsupportedOperation { operation: "serialize_unit_variant".to_string(), }) } fn serialize_newtype_struct( self, name: &'static str, value: &T, ) -> Result { Err(Error::UnsupportedOperation { operation: "serialize_newtype_struct".to_string(), }) } fn serialize_newtype_variant( self, name: &'static str, variant_index: u32, variant: &'static str, value: &T, ) -> Result { self.write_wrapped(variant, value) } fn serialize_seq(self, len: Option) -> Result { // TODO: Figure out how to constrain the things written to only be composites Err(Error::UnsupportedOperation { operation: "serialize_seq".to_string(), }) } fn serialize_tuple(self, len: usize) -> Result { Err(Error::UnsupportedOperation { operation: "serialize_tuple".to_string(), }) } fn serialize_tuple_struct( self, name: &'static str, len: usize, ) -> Result { Err(Error::UnsupportedOperation { operation: "serialize_tuple_struct".to_string(), }) } fn serialize_tuple_variant( self, name: &'static str, variant_index: u32, variant: &'static str, len: usize, ) -> Result { Err(Error::UnsupportedOperation { operation: "serialize_tuple_variant".to_string(), }) } fn serialize_map(self, len: Option) -> Result { Ok(Map::new(self)) } fn serialize_struct(self, name: &'static str, len: usize) -> Result { write!(self.writer, "<{}>", name)?; Ok(Struct::new(self, name)) } fn serialize_struct_variant( self, name: &'static str, variant_index: u32, variant: &'static str, len: usize, ) -> Result { Err(Error::UnsupportedOperation { operation: "Result".to_string(), }) } } #[cfg(test)] mod tests { use super::*; use serde::ser::{SerializeMap, SerializeStruct}; use serde::Serializer as SerSerializer; use serde_derive::Serialize; #[test] fn test_serialize_bool() { let inputs = vec![(true, "true"), (false, "false")]; for (src, should_be) in inputs { let mut buffer = Vec::new(); { let mut ser = Serializer::new(&mut buffer); ser.serialize_bool(src).unwrap(); } let got = String::from_utf8(buffer).unwrap(); assert_eq!(got, should_be); } } #[test] fn test_start_serialize_struct() { let mut buffer = Vec::new(); { let mut ser = Serializer::new(&mut buffer); let _ = ser.serialize_struct("foo", 0).unwrap(); } let got = String::from_utf8(buffer).unwrap(); assert_eq!(got, ""); } #[test] fn test_serialize_struct_field() { let mut buffer = Vec::new(); { let mut ser = Serializer::new(&mut buffer); let mut struct_ser = Struct::new(&mut ser, "baz"); struct_ser.serialize_field("foo", "bar").unwrap(); } let got = String::from_utf8(buffer).unwrap(); assert_eq!(got, "bar"); } #[test] fn test_serialize_struct() { #[derive(Serialize)] struct Person { name: String, age: u32, } let bob = Person { name: "Bob".to_string(), age: 42, }; let should_be = "Bob42"; let mut buffer = Vec::new(); { let mut ser = Serializer::new(&mut buffer); bob.serialize(&mut ser).unwrap(); } let got = String::from_utf8(buffer).unwrap(); assert_eq!(got, should_be); } #[test] fn test_serialize_map_entries() { let should_be = "Bob5"; let mut buffer = Vec::new(); { let mut ser = Serializer::new(&mut buffer); let mut map = Map::new(&mut ser); map.serialize_entry("name", "Bob").unwrap(); map.serialize_entry("age", "5").unwrap(); } let got = String::from_utf8(buffer).unwrap(); assert_eq!(got, should_be); } #[test] fn test_serialize_enum() { #[derive(Serialize)] #[allow(dead_code)] enum Node { Boolean(bool), Number(f64), String(String), } let mut buffer = Vec::new(); let should_be = "true"; { let mut ser = Serializer::new(&mut buffer); let node = Node::Boolean(true); node.serialize(&mut ser).unwrap(); } let got = String::from_utf8(buffer).unwrap(); assert_eq!(got, should_be); } #[test] #[ignore] fn serialize_a_list() { let inputs = vec![1, 2, 3, 4]; let mut buffer = Vec::new(); { let mut ser = Serializer::new(&mut buffer); inputs.serialize(&mut ser).unwrap(); } let got = String::from_utf8(buffer).unwrap(); println!("{}", got); panic!(); } } serde-xml-rs-0.5.1/src/ser/var.rs000064400000000000000000000045650072674642500147310ustar 00000000000000use std::io::Write; use serde::ser::{self, Serialize}; use crate::error::{Error, Result}; use crate::ser::Serializer; /// An implementation of `SerializeMap` for serializing to XML. pub struct Map<'w, W> where W: Write, { parent: &'w mut Serializer, } impl<'w, W> Map<'w, W> where W: 'w + Write, { pub fn new(parent: &'w mut Serializer) -> Map<'w, W> { Map { parent } } } impl<'w, W> ser::SerializeMap for Map<'w, W> where W: 'w + Write, { type Ok = (); type Error = Error; fn serialize_key(&mut self, _: &T) -> Result<()> { panic!("impossible to serialize the key on its own, please use serialize_entry()") } fn serialize_value(&mut self, value: &T) -> Result<()> { value.serialize(&mut *self.parent) } fn end(self) -> Result { Ok(()) } fn serialize_entry( &mut self, key: &K, value: &V, ) -> Result<()> { // TODO: Is it possible to ensure our key is never a composite type? // Anything which isn't a "primitive" would lead to malformed XML here... write!(self.parent.writer, "<")?; key.serialize(&mut *self.parent)?; write!(self.parent.writer, ">")?; value.serialize(&mut *self.parent)?; write!(self.parent.writer, "")?; Ok(()) } } /// An implementation of `SerializeStruct` for serializing to XML. pub struct Struct<'w, W> where W: Write, { parent: &'w mut Serializer, name: &'w str, } impl<'w, W> Struct<'w, W> where W: 'w + Write, { pub fn new(parent: &'w mut Serializer, name: &'w str) -> Struct<'w, W> { Struct { parent, name } } } impl<'w, W> ser::SerializeStruct for Struct<'w, W> where W: 'w + Write, { type Ok = (); type Error = Error; fn serialize_field( &mut self, key: &'static str, value: &T, ) -> Result<()> { write!(self.parent.writer, "<{}>", key)?; value.serialize(&mut *self.parent)?; write!(self.parent.writer, "", key)?; Ok(()) } fn end(self) -> Result { write!(self.parent.writer, "", self.name).map_err(|e| e.into()) } } serde-xml-rs-0.5.1/tests/failures.rs000064400000000000000000000020660072674642500155270ustar 00000000000000use log::info; use serde_derive::Deserialize; use serde_xml_rs::from_str; use simple_logger::SimpleLogger; fn init_logger() { let _ = SimpleLogger::new().init(); } #[derive(Debug, Deserialize, PartialEq)] struct Item { name: String, source: String, } #[test] fn simple_struct_from_attributes_should_fail() { init_logger(); let s = r##" = from_str(s); match item { Ok(_) => assert!(false), Err(e) => { info!("simple_struct_from_attributes_should_fail(): {}", e); assert!(true) } } } #[test] fn multiple_roots_attributes_should_fail() { init_logger(); let s = r##" "##; let item: Result, _> = from_str(s); match item { Ok(_) => assert!(false), Err(e) => { info!("multiple_roots_attributes_should_fail(): {}", e); assert!(true) } } } serde-xml-rs-0.5.1/tests/migrated.rs000064400000000000000000000633120072674642500155120ustar 00000000000000use simple_logger::SimpleLogger; use std::fmt::Debug; use serde::{de, ser}; use serde_derive::{Deserialize, Serialize}; use serde_xml_rs::{from_str, Error}; fn init_logger() { let _ = SimpleLogger::new().init(); } #[derive(PartialEq, Debug, Serialize, Deserialize)] enum Animal { Dog, Frog(String), Ant(Simple), Cat { age: usize, name: String }, } #[derive(PartialEq, Debug, Serialize, Deserialize)] struct Simple { a: (), b: usize, c: String, d: Option, } #[derive(PartialEq, Debug, Serialize, Deserialize)] struct Inner { a: (), b: (usize, String, i8), c: Vec, } #[derive(PartialEq, Debug, Serialize, Deserialize)] struct Outer { inner: Option, } fn test_parse_ok<'de, 'a, T>(errors: &[(&'a str, T)]) where T: PartialEq + Debug + ser::Serialize + de::Deserialize<'de>, { for &(s, ref value) in errors { let v: T = from_str(s).unwrap(); assert_eq!(v, *value); // // Make sure we can deserialize into an `Element`. // let xml_value: Element = from_str(s).unwrap(); // // Make sure we can deserialize from an `Element`. // let v: T = from_value(xml_value.clone()).unwrap(); // assert_eq!(v, *value); } } fn test_parse_err<'de, 'a, T>(errors: &[&'a str]) where T: PartialEq + Debug + ser::Serialize + de::Deserialize<'de>, { for &s in errors { assert!(match from_str::(s) { Err(Error::Syntax { source: _ }) => true, _ => false, }); } } fn test_parse_invalid<'de, 'a, T>(errors: &[&'a str]) where T: PartialEq + Debug + ser::Serialize + de::Deserialize<'de>, { for &s in errors { assert!(match from_str::(s) { Err(_) => true, _ => false, }); } } #[test] fn test_namespaces() { init_logger(); #[derive(PartialEq, Serialize, Deserialize, Debug)] struct Envelope { subject: String, } let s = r#" Reference rates "#; test_parse_ok(&[( s, Envelope { subject: "Reference rates".to_string(), }, )]); } #[test] fn test_doctype() { init_logger(); #[derive(PartialEq, Serialize, Deserialize, Debug)] struct Envelope { subject: String, } test_parse_ok(&[ ( r#" Reference rates "#, Envelope { subject: "Reference rates".to_string(), }, ), ( r#" Reference rates "#, Envelope { subject: "Reference rates".to_string(), }, ), ( r#" ] > Reference rates "#, Envelope { subject: "Reference rates".to_string(), }, ), ]); } #[test] #[ignore] // FIXME fn test_forwarded_namespace() { #[derive(PartialEq, Serialize, Deserialize, Debug)] struct Graphml { #[serde(rename = "xsi:schemaLocation")] schema_location: String, } let s = r#" "#; test_parse_ok(&[( s, Graphml { schema_location: "http://graphml.graphdrawing.org/xmlns http://graphml.graphdrawing.org/xmlns/1.0/graphml.xsd" .to_string(), }, )]); } #[test] fn test_parse_string() { init_logger(); test_parse_ok(&[ ( "This is a String", "This is a String".to_string(), ), ("", "".to_string()), (" ", "".to_string()), ("<boom/>", "".to_string()), ("", "♫".to_string()), ("", "♫".to_string()), ( "]]>♫", "♫♫".to_string(), ), ]); } #[test] #[ignore] // FIXME fn test_parse_string_not_trim() { init_logger(); test_parse_ok(&[(" ", " ".to_string())]); } #[test] #[ignore] // FIXME fn test_parse_enum() { use self::Animal::*; init_logger(); test_parse_ok(&[ ("", Dog), ( "Quak", Frog("Quak".to_string()), ), ( "bla15Foo", Ant(Simple { a: (), b: 15, c: "bla".to_string(), d: Some("Foo".to_string()), }), ), ( "bla15", Ant(Simple { a: (), b: 15, c: "bla".to_string(), d: None, }), ), ( "42Shere Khan", Cat { age: 42, name: "Shere Khan".to_string(), }, ), ]); #[derive(PartialEq, Debug, Serialize, Deserialize)] struct Helper { x: Animal, } test_parse_ok(&[ ("", Helper { x: Dog }), ( "Quak", Helper { x: Frog("Quak".to_string()), }, ), ( " 42 Shere Khan ", Helper { x: Cat { age: 42, name: "Shere Khan".to_string(), }, }, ), ]); } #[test] fn test_parse_i64() { init_logger(); test_parse_ok(&[ ("0", 0), ("-2", -2), ("-1234", -1234), (" -1234 ", -1234), ]); } #[test] fn test_parse_u64() { init_logger(); test_parse_ok(&[ ("0", 0), ("1234", 1234), (" 1234 ", 1234), ]); } #[test] fn test_parse_bool_element() { init_logger(); test_parse_ok(&[ ("true", true), ("false", false), (" true ", true), (" false ", false), ("1", true), ("0", false), ]); test_parse_invalid::(&["verum"]); } #[test] fn test_parse_bool_attribute() { #[derive(PartialEq, Debug, Deserialize, Serialize)] struct Dummy { foo: bool, } init_logger(); test_parse_ok(&[ ("", Dummy { foo: true }), ("", Dummy { foo: false }), ("", Dummy { foo: true }), ("", Dummy { foo: false }), ]); test_parse_invalid::(&[ "", "", "", "", "", ]); } #[test] fn test_parse_unit() { init_logger(); test_parse_ok(&[("", ())]); } #[test] fn test_parse_f64() { init_logger(); test_parse_ok(&[ ("3.0", 3.0f64), ("3.1", 3.1), ("-1.2", -1.2), ("0.4", 0.4), ("0.4e5", 0.4e5), ("0.4e15", 0.4e15), ("0.4e-01", 0.4e-01), // precision troubles (" 0.4e-01 ", 0.4e-01), ]); } #[test] fn test_parse_struct() { init_logger(); test_parse_ok(&[ ( " abc 2 ", Simple { a: (), b: 2, c: "abc".to_string(), d: None, }, ), ( " abc 2 ", Simple { a: (), b: 2, c: "abc".to_string(), d: None, }, ), ( " abc 2 ", Simple { a: (), b: 2, c: "abc".to_string(), d: Some("Foo".to_string()), }, ), ]); } #[test] fn test_option() { init_logger(); test_parse_ok(&[ ("", Some("".to_string())), ("", Some("".to_string())), (" ", Some("".to_string())), ("42", Some("42".to_string())), ]); } #[test] #[ignore] // FIXME fn test_option_not_trim() { init_logger(); test_parse_ok(&[(" ", Some(" ".to_string()))]); } #[test] fn test_amoskvin() { init_logger(); #[derive(Debug, Deserialize, PartialEq, Serialize)] struct Root { foo: Vec, } #[derive(Debug, Deserialize, PartialEq, Serialize)] struct Foo { a: String, b: Option, } test_parse_ok(&[( " Hello World Hi ", Root { foo: vec![ Foo { a: "Hello".to_string(), b: Some("World".to_string()), }, Foo { a: "Hi".to_string(), b: None, }, ], }, )]); } #[test] #[ignore] // FIXME fn test_nicolai86() { init_logger(); #[derive(Serialize, Deserialize, PartialEq, Debug)] struct TheSender { name: String, } #[derive(Serialize, Deserialize, PartialEq, Debug)] struct CurrencyCube { currency: String, rate: String, } #[derive(Serialize, Deserialize, PartialEq, Debug)] #[allow(non_snake_case)] struct InnerCube { Cube: Vec, } #[derive(Serialize, Deserialize, PartialEq, Debug)] #[allow(non_snake_case)] struct OuterCube { Cube: Vec, } #[derive(Serialize, Deserialize, PartialEq, Debug)] #[allow(non_snake_case)] struct Envelope { subject: String, Sender: TheSender, Cube: OuterCube, } test_parse_ok(&[ ( r#" Reference rates European Central Bank "#, Envelope { subject: "Reference rates".to_string(), Sender: TheSender { name: "European Central Bank".to_string(), }, Cube: OuterCube { Cube: vec![] }, }, ), ( r#" Reference rates European Central Bank "#, Envelope { subject: "Reference rates".to_string(), Sender: TheSender { name: "European Central Bank".to_string(), }, Cube: OuterCube { Cube: vec![InnerCube { Cube: vec![ CurrencyCube { currency: "GBP".to_string(), rate: "0.81725".to_string(), }, CurrencyCube { currency: "Latinum".to_string(), rate: "999999".to_string(), }, ], }], }, }, ), ]); } #[test] fn test_hugo_duncan2() { init_logger(); let s = r#" 8d521e9a-509e-4ef6-bbb7-9f1ac0d49cd1 vpc-ba0d18d8 available "#; #[derive(PartialEq, Debug, Serialize, Deserialize)] #[allow(non_snake_case)] struct VpcSet { vpcId: String, state: String, } #[derive(PartialEq, Debug, Serialize)] struct ItemVec(Vec); impl<'de, T: de::Deserialize<'de>> de::Deserialize<'de> for ItemVec { fn deserialize(deserializer: D) -> Result, D::Error> where D: de::Deserializer<'de>, { #[derive(PartialEq, Debug, Serialize, Deserialize)] struct Helper { item: Vec, } let h: Helper<_> = de::Deserialize::deserialize(deserializer)?; Ok(ItemVec(h.item)) } } #[derive(PartialEq, Debug, Serialize, Deserialize)] #[allow(non_snake_case)] struct DescribeVpcsResponse { requestId: String, vpcSet: ItemVec, } test_parse_ok(&[( s, DescribeVpcsResponse { requestId: "8d521e9a-509e-4ef6-bbb7-9f1ac0d49cd1".to_string(), vpcSet: ItemVec(vec![VpcSet { vpcId: "vpc-ba0d18d8".to_string(), state: "available".to_string(), }]), }, )]); } #[test] fn test_hugo_duncan() { init_logger(); let s = " 9474f558-10a5-42e8-84d1-f9ee181fe943 "; #[derive(PartialEq, Debug, Serialize, Deserialize)] #[allow(non_snake_case)] struct DescribeInstancesResponse { requestId: String, reservationSet: (), } test_parse_ok(&[( s, DescribeInstancesResponse { requestId: "9474f558-10a5-42e8-84d1-f9ee181fe943".to_string(), reservationSet: (), }, )]); } #[test] fn test_parse_xml_value() { init_logger(); #[derive(Eq, Debug, PartialEq, Deserialize, Serialize)] struct Test { #[serde(rename = "$value")] myval: String, } test_parse_ok(&[( "abc", Test { myval: "abc".to_string(), }, )]); } #[test] #[ignore] // FIXME fn test_parse_complexstruct() { init_logger(); test_parse_ok(&[ ( " 2 boom 88 ", Outer { inner: Some(Inner { a: (), b: (2, "boom".to_string(), 88), c: vec![], }), }, ), ( " abc xyz 2 boom 88 ", Outer { inner: Some(Inner { a: (), b: (2, "boom".to_string(), 88), c: vec!["abc".to_string(), "xyz".to_string()], }), }, ), ("", Outer { inner: None }), ]); } #[test] fn test_parse_attributes() { init_logger(); #[derive(PartialEq, Debug, Serialize, Deserialize)] struct A { a1: String, #[serde(rename = "$value")] a2: i32, } test_parse_ok(&[( r#"42"#, A { a1: "What is the answer to the ultimate question?".to_string(), a2: 42, }, )]); #[derive(PartialEq, Debug, Serialize, Deserialize)] struct B { b1: String, b2: i32, } test_parse_ok(&[( r#""#, B { b1: "What is the answer to the ultimate question?".to_string(), b2: 42, }, )]); #[derive(PartialEq, Debug, Serialize, Deserialize)] struct C { c1: B, } test_parse_ok(&[ ( r#""#, C { c1: B { b1: "What is the answer to the ultimate question?".to_string(), b2: 42, }, }, ), ( r#" "#, C { c1: B { b1: "What is the answer to the ultimate question?".to_string(), b2: 42, }, }, ), ( r#" "#, C { c1: B { b1: "What is the answer to the ultimate question?".to_string(), b2: 42, }, }, ), ]); #[derive(PartialEq, Debug, Serialize, Deserialize)] struct D { d1: Option, } test_parse_ok(&[( r#"42"#, D { d1: Some(A { a1: "What is the answer to the ultimate question?".to_string(), a2: 42, }), }, )]); } #[test] #[ignore] // FIXME fn test_parse_hierarchies() { init_logger(); #[derive(PartialEq, Debug, Serialize, Deserialize)] struct A { a1: String, a2: (String, String), } #[derive(PartialEq, Debug, Serialize, Deserialize)] struct B { b1: A, b2: (A, A), } #[derive(PartialEq, Debug, Serialize, Deserialize)] struct C { c1: B, c2: Vec, } test_parse_ok(&[ ( " No Maybe Yes Red Green Blue London Berlin Paris ", C { c1: B { b1: A { a1: "No".to_string(), a2: ("Maybe".to_string(), "Yes".to_string()), }, b2: ( A { a1: "Red".to_string(), a2: ("Green".to_string(), "Blue".to_string()), }, A { a1: "London".to_string(), a2: ("Berlin".to_string(), "Paris".to_string()), }, ), }, c2: vec![], }, ), ( " Green Blue Red Berlin Paris London Maybe Yes No ", C { c1: B { b1: A { a1: "No".to_string(), a2: ("Maybe".to_string(), "Yes".to_string()), }, b2: ( A { a1: "Red".to_string(), a2: ("Green".to_string(), "Blue".to_string()), }, A { a1: "London".to_string(), a2: ("Berlin".to_string(), "Paris".to_string()), }, ), }, c2: vec![], }, ), ]); } #[test] fn unknown_field() { #[derive(Deserialize, Debug, PartialEq, Eq, Serialize)] struct A { other: Vec, } #[derive(Deserialize, Debug, PartialEq, Eq, Serialize)] struct Other { d: i32, } test_parse_ok(&[( " 5 6 ", A { other: vec![Other { d: 6 }], }, )]); } // #[test] // fn eoz() { // use std::io::Read; // let mut file = std::fs::File::open("Report_test.2.xml").unwrap(); // let mut s = String::new(); // file.read_to_string(&mut s).unwrap(); // let _xml_value: Element = from_str(&s).unwrap(); // } #[test] fn test_parse_unfinished() { test_parse_err::(&[" abc 2 "]); } #[test] fn test_things_qc_found() { test_parse_err::(&["<\u{0}:/"]); } #[test] fn futile() { init_logger(); #[derive(Eq, PartialEq, Debug, Serialize, Deserialize)] struct Object { id: u8, name: String, x: u8, y: u8, width: u8, height: u8, ellipse: Option<()>, } test_parse_ok(&[ ( r###" "###, Object { id: 11, name: "testEllipse".to_owned(), x: 102, y: 38, width: 21, height: 14, ellipse: Some(()), }, ), ( r###" "###, Object { id: 11, name: "testEllipse".to_owned(), x: 102, y: 38, width: 21, height: 14, ellipse: None, }, ), ]); } #[test] fn futile2() { init_logger(); #[derive(Eq, PartialEq, Debug, Serialize, Deserialize)] struct Null; #[derive(Eq, PartialEq, Debug, Serialize, Deserialize)] struct Object { field: Option, }; #[derive(Eq, PartialEq, Debug, Serialize, Deserialize)] struct Stuff { stuff_field: Option, }; test_parse_ok(&[ ( r###" "###, Object { field: Some(Null) }, ), ( r###" "###, Object { field: None }, ), ]); test_parse_ok(&[ ( r###" "###, Stuff { stuff_field: Some(Object { field: None }), }, ), ( r###" "###, Stuff { stuff_field: Some(Object { field: Some(Null) }), }, ), ( r###" "###, Stuff { stuff_field: None }, ), ( r###" "###, Stuff { stuff_field: None }, ), ]); } #[test] fn newtype_struct() { #[derive(PartialEq, Debug, Serialize, Deserialize)] struct Wrapper(String); test_parse_ok(&[( r###"Content"###, Wrapper("Content".into()), )]); } serde-xml-rs-0.5.1/tests/round_trip.rs000064400000000000000000000053160072674642500161030ustar 00000000000000use serde::Deserialize; use serde_derive::{Deserialize, Serialize}; use serde_xml_rs::{self, from_str, to_string, EventReader, ParserConfig}; #[derive(Debug, Serialize, Deserialize, PartialEq)] struct Item { name: String, source: String, } #[derive(Debug, Serialize, Deserialize, PartialEq)] enum Node { Boolean(bool), Identifier { value: String, index: u32 }, EOF, } #[derive(Debug, Serialize, Deserialize, PartialEq)] struct Nodes { #[serde(rename = "$value")] items: Vec, } #[test] fn basic_struct() { let src = r#"BananaStore"#; let should_be = Item { name: "Banana".to_string(), source: "Store".to_string(), }; let item: Item = from_str(src).unwrap(); assert_eq!(item, should_be); let reserialized_item = to_string(&item).unwrap(); assert_eq!(src, reserialized_item); } #[test] #[ignore] fn round_trip_list_of_enums() { // Construct some inputs let nodes = Nodes { items: vec![ Node::Boolean(true), Node::Identifier { value: "foo".to_string(), index: 5, }, Node::EOF, ], }; let should_be = r#" true foo 5 "#; let serialized_nodes = to_string(&nodes).unwrap(); assert_eq!(serialized_nodes, should_be); // Then turn it back into a `Nodes` struct and make sure it's the same // as the original let deserialized_nodes: Nodes = from_str(serialized_nodes.as_str()).unwrap(); assert_eq!(deserialized_nodes, nodes); } #[test] fn whitespace_preserving_config() { // Test a configuration which does not clip whitespace from tags let src = r#" space banana fantasy costco "#; let item_should_be = Item { name: " space banana ".to_string(), source: " fantasy costco ".to_string(), }; let config = ParserConfig::new() .trim_whitespace(false) .whitespace_to_characters(false); let mut deserializer = serde_xml_rs::Deserializer::new(EventReader::new_with_config(src.as_bytes(), config)); let item = Item::deserialize(&mut deserializer).unwrap(); assert_eq!(item, item_should_be); // Space outside values is not preserved. let serialized_should_be = " space banana fantasy costco "; let reserialized_item = to_string(&item).unwrap(); assert_eq!(reserialized_item, serialized_should_be); } serde-xml-rs-0.5.1/tests/test.rs000064400000000000000000000203040072674642500146670ustar 00000000000000use serde::Deserialize; use serde_derive::Deserialize; use serde_xml_rs::{from_str, Deserializer}; use simple_logger::SimpleLogger; fn init_logger() { let _ = SimpleLogger::new().init(); } #[derive(Debug, Deserialize, PartialEq)] struct Item { name: String, source: String, } #[test] fn simple_struct_from_attributes() { init_logger(); let s = r##" "##; let item: Item = from_str(s).unwrap(); assert_eq!( item, Item { name: "hello".to_string(), source: "world.rs".to_string(), } ); } #[test] fn multiple_roots_attributes() { init_logger(); let s = r##" "##; let item: Vec = from_str(s).unwrap(); assert_eq!( item, vec![ Item { name: "hello".to_string(), source: "world.rs".to_string(), }, Item { name: "hello".to_string(), source: "world.rs".to_string(), }, ] ); } #[test] fn simple_struct_from_attribute_and_child() { init_logger(); let s = r##" world.rs "##; let item: Item = from_str(s).unwrap(); assert_eq!( item, Item { name: "hello".to_string(), source: "world.rs".to_string(), } ); } #[derive(Debug, Deserialize, PartialEq)] struct Project { name: String, #[serde(rename = "item", default)] items: Vec, } #[test] fn nested_collection() { init_logger(); let s = r##" "##; 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(), }, ], } ); } #[derive(Debug, Deserialize, PartialEq)] enum MyEnum { A(String), B { name: String, flag: bool }, C, } #[derive(Debug, Deserialize, PartialEq)] struct MyEnums { #[serde(rename = "$value")] items: Vec, } #[test] fn collection_of_enums() { init_logger(); let s = r##" test "##; let project: MyEnums = from_str(s).unwrap(); assert_eq!( project, MyEnums { items: vec![ MyEnum::A("test".to_string()), MyEnum::B { name: "hello".to_string(), flag: true, }, MyEnum::C, ], } ); } #[test] fn out_of_order_collection() { #[derive(Debug, Deserialize, PartialEq)] struct Collection { a: Vec, b: Vec, c: C, } #[derive(Debug, Deserialize, PartialEq)] struct A { name: String, } #[derive(Debug, Deserialize, PartialEq)] struct B { name: String, } #[derive(Debug, Deserialize, PartialEq)] struct C { name: String, } init_logger(); let in_xml = r#" "#; let should_be = Collection { a: vec![ A { name: "a1".into() }, A { name: "a2".into() }, A { name: "a3".into() }, A { name: "a4".into() }, ], b: vec![B { name: "b1".into() }, B { name: "b2".into() }], c: C { name: "c".into() }, }; let mut de = Deserializer::new_from_reader(in_xml.as_bytes()).non_contiguous_seq_elements(true); let actual = Collection::deserialize(&mut de).unwrap(); assert_eq!(should_be, actual); } #[test] fn nested_out_of_order_collection() { #[derive(Debug, Deserialize, PartialEq)] struct OuterCollection { a: A, inner: Vec, } #[derive(Debug, Deserialize, PartialEq)] struct InnerCollection { b: Vec, c: Vec, } #[derive(Debug, Deserialize, PartialEq)] struct A { name: String, } #[derive(Debug, Deserialize, PartialEq)] struct B { name: String, } #[derive(Debug, Deserialize, PartialEq)] struct C { name: String, } init_logger(); let in_xml = r#" "#; let should_be = OuterCollection { a: A { name: "a".into() }, inner: vec![ InnerCollection { b: vec![B { name: "b1".into() }, B { name: "b2".into() }], c: vec![C { name: "c1".into() }, C { name: "c2".into() }], }, InnerCollection { b: vec![B { name: "b3".into() }, B { name: "b4".into() }], c: vec![C { name: "c3".into() }, C { name: "c4".into() }], }, ], }; let mut de = Deserializer::new_from_reader(in_xml.as_bytes()).non_contiguous_seq_elements(true); let actual = OuterCollection::deserialize(&mut de).unwrap(); assert_eq!(should_be, actual); } #[test] fn out_of_order_tuple() { #[derive(Debug, Deserialize, PartialEq)] struct Collection { val: (A, B, C), other: A, } #[derive(Debug, Deserialize, PartialEq)] struct A { name_a: String, } #[derive(Debug, Deserialize, PartialEq)] struct B { name_b: String, } #[derive(Debug, Deserialize, PartialEq)] struct C { name_c: String, } init_logger(); let in_xml = r#" "#; let should_be = Collection { val: ( A { name_a: "a1".into(), }, B { name_b: "b".into() }, C { name_c: "c".into() }, ), other: A { name_a: "a2".into(), }, }; let mut de = Deserializer::new_from_reader(in_xml.as_bytes()).non_contiguous_seq_elements(true); let actual = Collection::deserialize(&mut de).unwrap(); assert_eq!(should_be, actual); } /// Ensure that identically-named elements at different depths are not deserialized as if they were /// at the same depth. #[test] fn nested_collection_repeated_elements() { #[derive(Debug, Deserialize, PartialEq)] struct OuterCollection { a: Vec, inner: Inner, } #[derive(Debug, Deserialize, PartialEq)] struct Inner { a: A, } #[derive(Debug, Deserialize, PartialEq)] struct A { name: String, } init_logger(); let in_xml = r#" "#; let should_be = OuterCollection { a: vec![A { name: "a1".into() }, A { name: "a3".into() }], inner: Inner { a: A { name: "a2".into() }, }, }; let mut de = Deserializer::new_from_reader(in_xml.as_bytes()).non_contiguous_seq_elements(true); let actual = OuterCollection::deserialize(&mut de).unwrap(); assert_eq!(should_be, actual); }