use crate::error::{Error, Result};
use crate::reader::{Attribute, XmlEvent, XmlReader};
use serde::de::{self, DeserializeSeed, MapAccess, SeqAccess, Visitor};
use std::borrow::Cow;
pub fn from_str<'de, T>(s: &'de str) -> Result<T>
where
T: de::Deserialize<'de>,
{
let mut de = Deserializer::from_str(s);
T::deserialize(&mut de)
}
pub fn from_bytes<'de, T>(bytes: &'de [u8]) -> Result<T>
where
T: de::Deserialize<'de>,
{
let s = std::str::from_utf8(bytes).map_err(|e| {
let offset = e.valid_up_to();
let mut line = 1;
let mut column = 1;
for &b in &bytes[..offset] {
if b == b'\n' {
line += 1;
column = 1;
} else if b & 0xC0 != 0x80 {
column += 1;
}
}
Error::new(crate::error::ErrorKind::InvalidUtf8)
.with_position(crate::error::Position { line, column, offset })
})?;
from_str(s)
}
pub struct Deserializer<'de> {
reader: XmlReader<'de>,
peeked: Option<XmlEvent<'de>>,
pending_value: Option<Cow<'de, str>>,
start_consumed: bool,
is_empty_element: bool,
text_key: &'static str,
pending_wrapper: bool,
}
impl<'de> Deserializer<'de> {
#[allow(clippy::should_implement_trait)]
pub fn from_str(s: &'de str) -> Self {
Self {
reader: XmlReader::from_str(s),
peeked: None,
pending_value: None,
start_consumed: false,
is_empty_element: false,
text_key: "$value",
pending_wrapper: false,
}
}
fn peek_event(&mut self) -> Result<&XmlEvent<'de>> {
if self.peeked.is_none() {
self.peeked = Some(self.reader.next_event()?);
}
Ok(self.peeked.as_ref().unwrap())
}
fn next_event(&mut self) -> Result<XmlEvent<'de>> {
if let Some(event) = self.peeked.take() {
Ok(event)
} else {
self.reader.next_event()
}
}
fn skip_ignorable_events(&mut self) -> Result<()> {
loop {
match self.peek_event()? {
XmlEvent::XmlDecl { .. }
| XmlEvent::Comment(_)
| XmlEvent::ProcessingInstruction { .. } => {
self.next_event()?;
}
_ => return Ok(()),
}
}
}
fn read_text_content(&mut self) -> Result<Cow<'de, str>> {
let mut content: Option<Cow<'de, str>> = None;
loop {
match self.peek_event()? {
XmlEvent::Text(_) | XmlEvent::CData(_) => {
let text = match self.next_event()? {
XmlEvent::Text(text) | XmlEvent::CData(text) => text,
_ => unreachable!(),
};
content = Some(match content {
None => text,
Some(existing) => {
let mut joined = existing.into_owned();
joined.push_str(&text);
Cow::Owned(joined)
}
});
}
XmlEvent::Comment(_) | XmlEvent::ProcessingInstruction { .. } => {
self.next_event()?;
}
_ => break,
}
}
Ok(content.unwrap_or(Cow::Borrowed("")))
}
fn read_element_text(&mut self) -> Result<Cow<'de, str>> {
if self.is_empty_element {
self.is_empty_element = false;
self.start_consumed = false;
return Ok(Cow::Borrowed(""));
}
let content = self.read_text_content()?;
if self.start_consumed {
self.start_consumed = false;
if let XmlEvent::EndElement { .. } = self.peek_event()? {
self.next_event()?;
}
}
Ok(content)
}
fn skip_element(&mut self) -> Result<()> {
self.drain_to_matching_end()
}
fn drain_to_matching_end(&mut self) -> Result<()> {
let mut depth = 0usize;
loop {
match self.peek_event()? {
XmlEvent::StartElement { .. } => {
depth += 1;
self.next_event()?;
}
XmlEvent::EndElement { .. } => {
self.next_event()?;
if depth == 0 {
return Ok(());
}
depth -= 1;
}
XmlEvent::Eof => {
return Err(Error::unexpected_eof().with_position(self.reader.position()))
}
_ => {
self.next_event()?;
}
}
}
}
fn parse_value<T>(&self, s: &str) -> Result<T>
where
T: std::str::FromStr,
T::Err: std::fmt::Display,
{
s.parse().map_err(|e: T::Err| {
Error::invalid_value(e.to_string()).with_position(self.reader.position())
})
}
fn get_text_cow(&mut self) -> Result<Cow<'de, str>> {
self.pending_wrapper = false;
if let Some(value) = self.pending_value.take() {
return Ok(value);
}
if self.start_consumed {
return self.read_element_text();
}
self.skip_ignorable_events()?;
match self.peek_event()? {
XmlEvent::StartElement { .. } => {
self.next_event()?;
self.start_consumed = true;
self.is_empty_element = false;
self.read_element_text()
}
XmlEvent::EmptyElement { .. } => {
self.next_event()?;
Ok(Cow::Borrowed(""))
}
_ => self.read_text_content(),
}
}
fn get_text(&mut self) -> Result<String> {
Ok(self.get_text_cow()?.into_owned())
}
}
fn text_key_for(fields: &[&'static str]) -> &'static str {
if fields.contains(&"$text") && !fields.contains(&"$value") {
"$text"
} else {
"$value"
}
}
impl<'de> de::Deserializer<'de> for &mut Deserializer<'de> {
type Error = Error;
fn deserialize_any<V>(self, visitor: V) -> Result<V::Value>
where
V: Visitor<'de>,
{
if let Some(value) = self.pending_value.take() {
return match value {
Cow::Borrowed(text) => visitor.visit_borrowed_str(text),
Cow::Owned(text) => visitor.visit_string(text),
};
}
match self.peek_event()? {
XmlEvent::StartElement { .. } | XmlEvent::EmptyElement { .. } => {
self.deserialize_map(visitor)
}
XmlEvent::Text(text) => {
let text = text.clone().into_owned();
self.next_event()?;
visitor.visit_string(text)
}
XmlEvent::CData(data) => {
let data = data.clone().into_owned();
self.next_event()?;
visitor.visit_string(data)
}
XmlEvent::EndElement { .. } => visitor.visit_unit(),
XmlEvent::Eof => visitor.visit_unit(),
_ => {
self.next_event()?;
self.deserialize_any(visitor)
}
}
}
fn deserialize_bool<V>(self, visitor: V) -> Result<V::Value>
where
V: Visitor<'de>,
{
let text = self.get_text_cow()?;
match &*text {
"true" | "1" | "yes" => visitor.visit_bool(true),
"false" | "0" | "no" => visitor.visit_bool(false),
_ => Err(Error::invalid_value(format!("expected boolean, got '{}'", text))
.with_position(self.reader.position())),
}
}
fn deserialize_i8<V>(self, visitor: V) -> Result<V::Value>
where
V: Visitor<'de>,
{
let text = self.get_text_cow()?;
visitor.visit_i8(self.parse_value(&text)?)
}
fn deserialize_i16<V>(self, visitor: V) -> Result<V::Value>
where
V: Visitor<'de>,
{
let text = self.get_text_cow()?;
visitor.visit_i16(self.parse_value(&text)?)
}
fn deserialize_i32<V>(self, visitor: V) -> Result<V::Value>
where
V: Visitor<'de>,
{
let text = self.get_text_cow()?;
visitor.visit_i32(self.parse_value(&text)?)
}
fn deserialize_i64<V>(self, visitor: V) -> Result<V::Value>
where
V: Visitor<'de>,
{
let text = self.get_text_cow()?;
visitor.visit_i64(self.parse_value(&text)?)
}
fn deserialize_u8<V>(self, visitor: V) -> Result<V::Value>
where
V: Visitor<'de>,
{
let text = self.get_text_cow()?;
visitor.visit_u8(self.parse_value(&text)?)
}
fn deserialize_u16<V>(self, visitor: V) -> Result<V::Value>
where
V: Visitor<'de>,
{
let text = self.get_text_cow()?;
visitor.visit_u16(self.parse_value(&text)?)
}
fn deserialize_u32<V>(self, visitor: V) -> Result<V::Value>
where
V: Visitor<'de>,
{
let text = self.get_text_cow()?;
visitor.visit_u32(self.parse_value(&text)?)
}
fn deserialize_u64<V>(self, visitor: V) -> Result<V::Value>
where
V: Visitor<'de>,
{
let text = self.get_text_cow()?;
visitor.visit_u64(self.parse_value(&text)?)
}
fn deserialize_f32<V>(self, visitor: V) -> Result<V::Value>
where
V: Visitor<'de>,
{
let text = self.get_text_cow()?;
visitor.visit_f32(self.parse_value(&text)?)
}
fn deserialize_f64<V>(self, visitor: V) -> Result<V::Value>
where
V: Visitor<'de>,
{
let text = self.get_text_cow()?;
visitor.visit_f64(self.parse_value(&text)?)
}
fn deserialize_char<V>(self, visitor: V) -> Result<V::Value>
where
V: Visitor<'de>,
{
let text = self.get_text_cow()?;
let mut chars = text.chars();
match (chars.next(), chars.next()) {
(Some(c), None) => visitor.visit_char(c),
_ => Err(Error::invalid_value("expected single character")
.with_position(self.reader.position())),
}
}
fn deserialize_str<V>(self, visitor: V) -> Result<V::Value>
where
V: Visitor<'de>,
{
match self.get_text_cow()? {
Cow::Borrowed(text) => visitor.visit_borrowed_str(text),
Cow::Owned(text) => visitor.visit_string(text),
}
}
fn deserialize_string<V>(self, visitor: V) -> Result<V::Value>
where
V: Visitor<'de>,
{
let text = self.get_text()?;
visitor.visit_string(text)
}
fn deserialize_bytes<V>(self, visitor: V) -> Result<V::Value>
where
V: Visitor<'de>,
{
match self.get_text_cow()? {
Cow::Borrowed(text) => visitor.visit_borrowed_bytes(text.as_bytes()),
Cow::Owned(text) => visitor.visit_byte_buf(text.into_bytes()),
}
}
fn deserialize_byte_buf<V>(self, visitor: V) -> Result<V::Value>
where
V: Visitor<'de>,
{
self.deserialize_bytes(visitor)
}
fn deserialize_option<V>(self, visitor: V) -> Result<V::Value>
where
V: Visitor<'de>,
{
if self.pending_value.is_some() || self.start_consumed {
return visitor.visit_some(self);
}
match self.peek_event()? {
XmlEvent::EndElement { .. } | XmlEvent::Eof => visitor.visit_none(),
_ => visitor.visit_some(self),
}
}
fn deserialize_unit<V>(self, visitor: V) -> Result<V::Value>
where
V: Visitor<'de>,
{
self.pending_value = None;
self.pending_wrapper = false;
if self.start_consumed && !self.is_empty_element {
if let XmlEvent::EndElement { .. } = self.peek_event()? {
self.next_event()?;
}
}
self.start_consumed = false;
self.is_empty_element = false;
visitor.visit_unit()
}
fn deserialize_unit_struct<V>(self, _name: &'static str, visitor: V) -> Result<V::Value>
where
V: Visitor<'de>,
{
self.pending_wrapper = false;
if self.start_consumed {
if !self.is_empty_element {
self.drain_to_matching_end()?;
}
self.start_consumed = false;
self.is_empty_element = false;
return visitor.visit_unit();
}
match self.peek_event()? {
XmlEvent::EmptyElement { .. } => {
self.next_event()?;
}
XmlEvent::StartElement { .. } => {
self.next_event()?;
self.drain_to_matching_end()?;
}
_ => {}
}
visitor.visit_unit()
}
fn deserialize_newtype_struct<V>(self, _name: &'static str, visitor: V) -> Result<V::Value>
where
V: Visitor<'de>,
{
visitor.visit_newtype_struct(self)
}
fn deserialize_seq<V>(self, visitor: V) -> Result<V::Value>
where
V: Visitor<'de>,
{
visitor.visit_seq(SeqDeserializer::new(self))
}
fn deserialize_tuple<V>(self, _len: usize, visitor: V) -> Result<V::Value>
where
V: Visitor<'de>,
{
self.deserialize_seq(visitor)
}
fn deserialize_tuple_struct<V>(
self,
_name: &'static str,
_len: usize,
visitor: V,
) -> Result<V::Value>
where
V: Visitor<'de>,
{
self.deserialize_seq(visitor)
}
fn deserialize_map<V>(self, visitor: V) -> Result<V::Value>
where
V: Visitor<'de>,
{
self.pending_wrapper = false;
let text_key = std::mem::replace(&mut self.text_key, "$value");
let (attrs, is_empty) = if self.start_consumed {
self.start_consumed = false;
let is_empty = self.is_empty_element;
self.is_empty_element = false;
(vec![], is_empty)
} else {
self.skip_ignorable_events()?;
match self.next_event()? {
XmlEvent::StartElement { attributes, .. } => (attributes, false),
XmlEvent::EmptyElement { attributes, .. } => (attributes, true),
XmlEvent::Eof => {
return Err(Error::unexpected_eof().with_position(self.reader.position()))
}
_ => (vec![], false),
}
};
let result = visitor.visit_map(MapDeserializer {
de: self,
attrs,
attr_idx: 0,
finished: is_empty,
text_key,
})?;
if !is_empty {
self.drain_to_matching_end()?;
}
Ok(result)
}
fn deserialize_struct<V>(
self,
_name: &'static str,
fields: &'static [&'static str],
visitor: V,
) -> Result<V::Value>
where
V: Visitor<'de>,
{
self.text_key = text_key_for(fields);
self.deserialize_map(visitor)
}
fn deserialize_enum<V>(
self,
_name: &'static str,
_variants: &'static [&'static str],
visitor: V,
) -> Result<V::Value>
where
V: Visitor<'de>,
{
if std::mem::take(&mut self.pending_wrapper) && self.pending_value.is_none() {
self.skip_ignorable_events()?;
if matches!(self.peek_event()?, XmlEvent::EmptyElement { .. }) {
let name = match self.next_event()? {
XmlEvent::EmptyElement { name, .. } => name.into_owned(),
_ => unreachable!(),
};
return Err(Error::custom(format!(
"cannot deserialize enum from empty element `<{}/>`",
name
))
.with_position(self.reader.position()));
}
if matches!(self.peek_event()?, XmlEvent::StartElement { .. }) {
self.next_event()?;
let value = visitor.visit_enum(EnumDeserializer::new(&mut *self))?;
self.drain_to_matching_end()?;
return Ok(value);
}
}
visitor.visit_enum(EnumDeserializer::new(self))
}
fn deserialize_identifier<V>(self, visitor: V) -> Result<V::Value>
where
V: Visitor<'de>,
{
self.deserialize_string(visitor)
}
fn deserialize_ignored_any<V>(self, visitor: V) -> Result<V::Value>
where
V: Visitor<'de>,
{
self.pending_value = None;
self.pending_wrapper = false;
if self.start_consumed {
if !self.is_empty_element {
self.skip_element()?;
}
self.start_consumed = false;
self.is_empty_element = false;
return visitor.visit_unit();
}
match self.peek_event()? {
XmlEvent::StartElement { .. } => {
self.next_event()?;
self.skip_element()?;
}
XmlEvent::EmptyElement { .. } => {
self.next_event()?;
}
XmlEvent::Text(_) | XmlEvent::CData(_) => {
self.next_event()?;
}
_ => {}
}
visitor.visit_unit()
}
}
struct SeqDeserializer<'a, 'de> {
de: &'a mut Deserializer<'de>,
element_name: Option<String>,
}
impl<'a, 'de> SeqDeserializer<'a, 'de> {
fn new(de: &'a mut Deserializer<'de>) -> Self {
Self {
de,
element_name: None,
}
}
}
impl<'de, 'a> SeqAccess<'de> for SeqDeserializer<'a, 'de> {
type Error = Error;
fn next_element_seed<T>(&mut self, seed: T) -> Result<Option<T::Value>>
where
T: DeserializeSeed<'de>,
{
loop {
match self.de.peek_event()? {
XmlEvent::StartElement { name, .. } | XmlEvent::EmptyElement { name, .. } => {
if let Some(ref expected) = self.element_name {
if name.as_ref() != expected.as_str() {
return Ok(None);
}
} else {
self.element_name = Some(name.clone().into_owned());
}
self.de.pending_wrapper = true;
return seed.deserialize(&mut *self.de).map(Some);
}
XmlEvent::EndElement { .. } | XmlEvent::Eof => return Ok(None),
XmlEvent::Text(_) | XmlEvent::CData(_) => {
return seed.deserialize(&mut *self.de).map(Some);
}
_ => {
self.de.next_event()?;
}
}
}
}
}
struct MapDeserializer<'a, 'de> {
de: &'a mut Deserializer<'de>,
attrs: Vec<Attribute<'de>>,
attr_idx: usize,
finished: bool,
text_key: &'static str,
}
impl<'de, 'a> MapAccess<'de> for MapDeserializer<'a, 'de> {
type Error = Error;
fn next_key_seed<K>(&mut self, seed: K) -> Result<Option<K::Value>>
where
K: DeserializeSeed<'de>,
{
if self.attr_idx < self.attrs.len() {
let name = &self.attrs[self.attr_idx].name;
let mut key = String::with_capacity(name.len() + 1);
key.push('@');
key.push_str(name);
self.de.pending_value = Some(Cow::Owned(key));
return seed.deserialize(&mut *self.de).map(Some);
}
if self.finished {
return Ok(None);
}
loop {
match self.de.peek_event()? {
XmlEvent::StartElement { name, .. } | XmlEvent::EmptyElement { name, .. } => {
let name = name.clone();
self.de.pending_value = Some(name);
return seed.deserialize(&mut *self.de).map(Some);
}
XmlEvent::EndElement { .. } | XmlEvent::Eof => {
self.finished = true;
return Ok(None);
}
XmlEvent::Text(_) | XmlEvent::CData(_) => {
self.de.pending_value = Some(Cow::Borrowed(self.text_key));
return seed.deserialize(&mut *self.de).map(Some);
}
_ => {
self.de.next_event()?;
}
}
}
}
fn next_value_seed<V>(&mut self, seed: V) -> Result<V::Value>
where
V: DeserializeSeed<'de>,
{
if self.attr_idx < self.attrs.len() {
let value = &self.attrs[self.attr_idx].value;
self.attr_idx += 1;
self.de.pending_value = Some(value.clone());
return seed.deserialize(&mut *self.de);
}
self.de.pending_wrapper = true;
seed.deserialize(&mut *self.de)
}
}
struct EnumDeserializer<'a, 'de> {
de: &'a mut Deserializer<'de>,
}
impl<'a, 'de> EnumDeserializer<'a, 'de> {
fn new(de: &'a mut Deserializer<'de>) -> Self {
Self { de }
}
}
impl<'de, 'a> de::EnumAccess<'de> for EnumDeserializer<'a, 'de> {
type Error = Error;
type Variant = Self;
fn variant_seed<V>(self, seed: V) -> Result<(V::Value, Self::Variant)>
where
V: DeserializeSeed<'de>,
{
if self.de.pending_value.is_some() {
let variant = seed.deserialize(&mut *self.de)?;
return Ok((variant, self));
}
match self.de.peek_event()? {
XmlEvent::StartElement { name, .. } | XmlEvent::EmptyElement { name, .. } => {
let name = name.clone();
self.de.pending_value = Some(name);
}
XmlEvent::Text(text) => {
let text = text.clone();
self.de.pending_value = Some(text);
}
_ => {}
}
let variant = seed.deserialize(&mut *self.de)?;
Ok((variant, self))
}
}
impl<'de, 'a> de::VariantAccess<'de> for EnumDeserializer<'a, 'de> {
type Error = Error;
fn unit_variant(self) -> Result<()> {
if self.de.start_consumed {
if !self.de.is_empty_element {
self.de.skip_element()?;
}
self.de.start_consumed = false;
self.de.is_empty_element = false;
return Ok(());
}
match self.de.peek_event()? {
XmlEvent::EmptyElement { .. } => {
self.de.next_event()?;
}
XmlEvent::StartElement { .. } => {
self.de.next_event()?;
self.de.skip_element()?;
}
_ => {}
}
Ok(())
}
fn newtype_variant_seed<T>(self, seed: T) -> Result<T::Value>
where
T: DeserializeSeed<'de>,
{
self.de.pending_wrapper = true;
seed.deserialize(&mut *self.de)
}
fn tuple_variant<V>(self, _len: usize, visitor: V) -> Result<V::Value>
where
V: Visitor<'de>,
{
if matches!(self.de.peek_event()?, XmlEvent::StartElement { .. }) {
self.de.next_event()?;
let value = de::Deserializer::deserialize_seq(&mut *self.de, visitor)?;
if let XmlEvent::EndElement { .. } = self.de.peek_event()? {
self.de.next_event()?;
}
return Ok(value);
}
de::Deserializer::deserialize_seq(&mut *self.de, visitor)
}
fn struct_variant<V>(self, fields: &'static [&'static str], visitor: V) -> Result<V::Value>
where
V: Visitor<'de>,
{
self.de.text_key = text_key_for(fields);
de::Deserializer::deserialize_map(&mut *self.de, visitor)
}
}
#[cfg(test)]
mod tests {
use super::*;
use serde::{Deserialize, Serialize};
#[test]
fn test_deserialize_simple_struct() {
#[derive(Debug, Deserialize, PartialEq)]
struct Person {
name: String,
age: u32,
}
let xml = "<Person><name>Alice</name><age>30</age></Person>";
let person: Person = from_str(xml).unwrap();
assert_eq!(person.name, "Alice");
assert_eq!(person.age, 30);
}
#[test]
fn test_deserialize_with_attributes() {
#[derive(Debug, Deserialize, PartialEq)]
struct Item {
#[serde(rename = "@id")]
id: String,
name: String,
}
let xml = r#"<Item id="123"><name>Widget</name></Item>"#;
let item: Item = from_str(xml).unwrap();
assert_eq!(item.id, "123");
assert_eq!(item.name, "Widget");
}
#[test]
fn test_deserialize_nested_struct() {
#[derive(Debug, Deserialize, PartialEq)]
struct Address {
city: String,
country: String,
}
#[derive(Debug, Deserialize, PartialEq)]
struct Person {
name: String,
address: Address,
}
let xml = r#"
<Person>
<name>Bob</name>
<address>
<city>New York</city>
<country>USA</country>
</address>
</Person>
"#;
let person: Person = from_str(xml).unwrap();
assert_eq!(person.name, "Bob");
assert_eq!(person.address.city, "New York");
}
#[test]
fn test_deserialize_optional() {
#[derive(Debug, Deserialize, PartialEq)]
struct Config {
name: String,
#[serde(default)]
value: Option<String>,
}
let xml1 = "<Config><name>test</name><value>val</value></Config>";
let config1: Config = from_str(xml1).unwrap();
assert_eq!(config1.value, Some("val".to_string()));
let xml2 = "<Config><name>test</name></Config>";
let config2: Config = from_str(xml2).unwrap();
assert_eq!(config2.value, None);
}
#[test]
fn test_deserialize_bool() {
#[derive(Debug, Deserialize, PartialEq)]
struct Flags {
enabled: bool,
active: bool,
}
let xml = "<Flags><enabled>true</enabled><active>false</active></Flags>";
let flags: Flags = from_str(xml).unwrap();
assert!(flags.enabled);
assert!(!flags.active);
}
#[test]
fn test_deserialize_numbers() {
#[derive(Debug, Deserialize, PartialEq)]
struct Numbers {
i: i32,
u: u64,
f: f64,
}
let xml = "<Numbers><i>-42</i><u>100</u><f>1.234</f></Numbers>";
let nums: Numbers = from_str(xml).unwrap();
assert_eq!(nums.i, -42);
assert_eq!(nums.u, 100);
assert!((nums.f - 1.234).abs() < 0.001);
}
#[test]
fn test_deserialize_vector() {
#[derive(Debug, Deserialize, PartialEq)]
struct Items {
item: Vec<String>,
}
let xml = r#"<Items><item>one</item><item>two</item><item>three</item></Items>"#;
let items: Items = from_str(xml).unwrap();
assert_eq!(items.item, vec!["one", "two", "three"]);
}
#[test]
fn test_deserialize_escaped_content() {
#[derive(Debug, Deserialize, PartialEq)]
struct Data {
content: String,
}
let xml = "<Data><content><hello> & "world"</content></Data>";
let data: Data = from_str(xml).unwrap();
assert_eq!(data.content, "<hello> & \"world\"");
}
#[test]
fn test_deserialize_empty_element() {
#[derive(Debug, Deserialize, PartialEq)]
struct Empty {
#[serde(default)]
value: String,
}
let xml = "<Empty><value></value></Empty>";
let empty: Empty = from_str(xml).unwrap();
assert_eq!(empty.value, "");
}
#[test]
fn test_deserialize_char() {
#[derive(Debug, Deserialize, PartialEq)]
struct Data {
c: char,
}
let xml = "<Data><c>A</c></Data>";
let data: Data = from_str(xml).unwrap();
assert_eq!(data.c, 'A');
}
#[test]
fn test_deserialize_unit_struct() {
#[derive(Debug, Deserialize, PartialEq)]
struct Unit;
let xml = "<Unit/>";
let _unit: Unit = from_str(xml).unwrap();
}
#[test]
fn test_from_bytes() {
#[derive(Debug, Deserialize, PartialEq)]
struct Data {
value: String,
}
let bytes = b"<Data><value>test</value></Data>";
let data: Data = from_bytes(bytes).unwrap();
assert_eq!(data.value, "test");
}
#[test]
fn test_deserialize_vector_of_structs() {
#[derive(Debug, Deserialize, PartialEq)]
struct Item {
name: String,
count: u32,
}
#[derive(Debug, Deserialize, PartialEq)]
struct Items {
item: Vec<Item>,
}
let xml = r#"<Items><item><name>A</name><count>1</count></item><item><name>B</name><count>2</count></item></Items>"#;
let items: Items = from_str(xml).unwrap();
assert_eq!(items.item.len(), 2);
assert_eq!(items.item[0].name, "A");
assert_eq!(items.item[1].name, "B");
}
#[test]
fn test_deserialize_multiple_attributes() {
#[derive(Debug, Deserialize, PartialEq)]
struct Element {
#[serde(rename = "@id")]
id: String,
#[serde(rename = "@class")]
class: String,
name: String,
}
let xml = r#"<Element id="main" class="container"><name>Test</name></Element>"#;
let elem: Element = from_str(xml).unwrap();
assert_eq!(elem.id, "main");
assert_eq!(elem.class, "container");
assert_eq!(elem.name, "Test");
}
#[test]
fn test_deserialize_attributes_with_special_chars() {
#[derive(Debug, Deserialize, PartialEq)]
struct Element {
#[serde(rename = "@title")]
title: String,
}
let xml = r#"<Element title="Hello & "World""/>"#;
let elem: Element = from_str(xml).unwrap();
assert_eq!(elem.title, "Hello & \"World\"");
}
#[test]
fn test_deserialize_numeric_attributes() {
#[derive(Debug, Deserialize, PartialEq)]
struct Item {
#[serde(rename = "@id")]
id: u32,
#[serde(rename = "@count")]
count: i32,
#[serde(rename = "@price")]
price: f64,
#[serde(rename = "@active")]
active: bool,
}
let xml = r#"<Item id="42" count="-10" price="19.99" active="true"/>"#;
let item: Item = from_str(xml).unwrap();
assert_eq!(item.id, 42);
assert_eq!(item.count, -10);
assert!((item.price - 19.99).abs() < 0.001);
assert!(item.active);
}
#[test]
fn test_deserialize_empty_element_with_attributes() {
#[derive(Debug, Deserialize, PartialEq)]
struct Empty {
#[serde(rename = "@id")]
id: String,
#[serde(default)]
value: String,
}
let xml = r#"<Empty id="test"/>"#;
let elem: Empty = from_str(xml).unwrap();
assert_eq!(elem.id, "test");
assert_eq!(elem.value, "");
}
#[test]
fn test_deserialize_nested_with_attributes() {
#[derive(Debug, Deserialize, PartialEq)]
struct Child {
#[serde(rename = "@name")]
name: String,
}
#[derive(Debug, Deserialize, PartialEq)]
struct Parent {
#[serde(rename = "@id")]
id: String,
child: Child,
}
let xml = r#"<Parent id="p1"><child name="c1"/></Parent>"#;
let parent: Parent = from_str(xml).unwrap();
assert_eq!(parent.id, "p1");
assert_eq!(parent.child.name, "c1");
}
#[test]
fn test_deserialize_vector_with_attributes() {
#[derive(Debug, Deserialize, PartialEq)]
struct Item {
#[serde(rename = "@id")]
id: u32,
name: String,
}
#[derive(Debug, Deserialize, PartialEq)]
struct List {
#[serde(rename = "@version")]
version: String,
item: Vec<Item>,
}
let xml = r#"<List version="1.0"><item id="1"><name>A</name></item><item id="2"><name>B</name></item></List>"#;
let list: List = from_str(xml).unwrap();
assert_eq!(list.version, "1.0");
assert_eq!(list.item.len(), 2);
assert_eq!(list.item[0].id, 1);
assert_eq!(list.item[1].id, 2);
}
#[test]
fn test_deserialize_value_text_with_attribute() {
#[derive(Debug, Deserialize, PartialEq)]
struct Link {
#[serde(rename = "@href")]
href: String,
#[serde(rename = "$value")]
value: String,
}
let xml = r#"<Link href="https://example.com">Example</Link>"#;
let link: Link = from_str(xml).unwrap();
assert_eq!(link.href, "https://example.com");
assert_eq!(link.value, "Example");
}
#[test]
fn test_round_trip_text_rename_with_attribute() {
#[derive(Debug, Serialize, Deserialize, PartialEq)]
struct Link {
#[serde(rename = "@href")]
href: String,
#[serde(rename = "$text")]
text: String,
}
let link = Link {
href: "https://example.com".to_string(),
text: "Example".to_string(),
};
let xml = crate::to_string(&link).unwrap();
let parsed: Link = from_str(&xml).unwrap();
assert_eq!(parsed, link);
}
#[test]
fn test_deserialize_text_rename() {
#[derive(Debug, Deserialize, PartialEq)]
struct Link {
#[serde(rename = "@href")]
href: String,
#[serde(rename = "$text")]
text: String,
}
let xml = r#"<Link href="https://example.com">Example</Link>"#;
let link: Link = from_str(xml).unwrap();
assert_eq!(link.href, "https://example.com");
assert_eq!(link.text, "Example");
}
#[test]
fn test_invalid_number_error_has_position() {
#[derive(Debug, Deserialize)]
struct Person {
age: u32,
}
let xml = "<Person><age>abc</age></Person>";
let err = from_str::<Person>(xml).unwrap_err();
let pos = err.position().expect("error should carry a position");
assert_eq!(pos.line, 1);
assert_eq!(pos.column, 23);
assert_eq!(pos.offset, 22);
let person: Person = from_str("<Person><age>30</age></Person>").unwrap();
assert_eq!(person.age, 30);
}
#[test]
fn test_deserialize_borrowed_str() {
#[derive(Debug, Deserialize, PartialEq)]
struct Borrowed<'a> {
#[serde(borrow)]
name: &'a str,
}
let xml = "<Borrowed><name>hi</name></Borrowed>";
let borrowed: Borrowed<'_> = from_str(xml).unwrap();
assert_eq!(borrowed.name, "hi");
}
#[test]
fn test_deserialize_skips_comments_and_pis() {
#[derive(Debug, Deserialize, PartialEq)]
struct Person {
name: String,
age: u32,
}
let xml = "<Person><!-- a comment --><name>Alice</name><?pi data?><age>30</age><!-- trailing --></Person>";
let person: Person = from_str(xml).unwrap();
assert_eq!(person.name, "Alice");
assert_eq!(person.age, 30);
}
#[test]
fn test_invalid_bool_error_has_position() {
#[derive(Debug, Deserialize)]
struct Flags {
b: bool,
}
let xml = "<a><b>maybe</b></a>";
let err = from_str::<Flags>(xml).unwrap_err();
let pos = err.position().expect("error should carry a position");
assert_eq!(pos.line, 1);
assert_eq!(pos.column, 16);
assert_eq!(pos.offset, 15);
let flags: Flags = from_str("<a><b>yes</b></a>").unwrap();
assert!(flags.b);
}
#[test]
fn test_invalid_char_error_has_position() {
#[derive(Debug, Deserialize)]
struct Data {
c: char,
}
let xml = "<Data><c>abc</c></Data>";
let err = from_str::<Data>(xml).unwrap_err();
let pos = err.position().expect("error should carry a position");
assert_eq!(pos.line, 1);
assert_eq!(pos.column, 17);
assert_eq!(pos.offset, 16);
let data: Data = from_str("<Data><c>x</c></Data>").unwrap();
assert_eq!(data.c, 'x');
}
#[test]
fn test_deserialize_borrowed_attribute() {
#[derive(Debug, Deserialize, PartialEq)]
struct Tag<'a> {
#[serde(rename = "@href", borrow)]
href: &'a str,
}
let xml = r#"<Tag href="x"/>"#;
let tag: Tag<'_> = from_str(xml).unwrap();
assert_eq!(tag.href, "x");
}
#[test]
fn test_deserialize_escaped_attribute_falls_back_to_owned() {
#[derive(Debug, Deserialize, PartialEq)]
struct Tag<'a> {
#[serde(rename = "@href", borrow)]
href: Cow<'a, str>,
}
let xml = r#"<Tag href="a & b"/>"#;
let tag: Tag<'_> = from_str(xml).unwrap();
assert_eq!(tag.href, "a & b");
assert!(matches!(tag.href, Cow::Owned(_)));
}
#[test]
fn test_deserialize_comment_before_root() {
#[derive(Debug, Deserialize, PartialEq)]
struct Person {
name: String,
age: u32,
}
let xml = "<!-- leading --><Person><name>Alice</name><age>30</age></Person>";
let person: Person = from_str(xml).unwrap();
assert_eq!(person.name, "Alice");
assert_eq!(person.age, 30);
}
#[test]
fn test_deserialize_pi_before_root() {
#[derive(Debug, Deserialize, PartialEq)]
struct Person {
name: String,
age: u32,
}
let xml = "<?pi data?><Person><name>Alice</name><age>30</age></Person>";
let person: Person = from_str(xml).unwrap();
assert_eq!(person.name, "Alice");
assert_eq!(person.age, 30);
}
#[test]
fn test_deserialize_xml_decl_before_root() {
#[derive(Debug, Deserialize, PartialEq)]
struct Person {
name: String,
age: u32,
}
let xml = r#"<?xml version="1.0"?><Person><name>Alice</name><age>30</age></Person>"#;
let person: Person = from_str(xml).unwrap();
assert_eq!(person.name, "Alice");
assert_eq!(person.age, 30);
}
#[test]
fn test_round_trip_enum_field_unit_variant() {
#[derive(Debug, Serialize, Deserialize, PartialEq)]
enum Status {
Active,
Inactive,
}
#[derive(Debug, Serialize, Deserialize, PartialEq)]
struct Item {
status: Status,
name: String,
}
let item = Item {
status: Status::Active,
name: "widget".to_string(),
};
let xml = crate::to_string(&item).unwrap();
let parsed: Item = from_str(&xml).unwrap();
assert_eq!(parsed, item);
let item = Item {
status: Status::Inactive,
name: "gadget".to_string(),
};
let xml = crate::to_string(&item).unwrap();
let parsed: Item = from_str(&xml).unwrap();
assert_eq!(parsed, item);
}
#[test]
fn test_round_trip_enum_field_newtype_variant() {
#[derive(Debug, Serialize, Deserialize, PartialEq)]
enum Status {
Count(u32),
}
#[derive(Debug, Serialize, Deserialize, PartialEq)]
struct Item {
status: Status,
name: String,
}
let item = Item {
status: Status::Count(42),
name: "widget".to_string(),
};
let xml = crate::to_string(&item).unwrap();
let parsed: Item = from_str(&xml).unwrap();
assert_eq!(parsed, item);
}
#[test]
fn test_round_trip_enum_field_tuple_variant() {
#[derive(Debug, Serialize, Deserialize, PartialEq)]
enum Status {
Pair(u32, String),
}
#[derive(Debug, Serialize, Deserialize, PartialEq)]
struct Item {
status: Status,
name: String,
}
let item = Item {
status: Status::Pair(1, "one".to_string()),
name: "widget".to_string(),
};
let xml = crate::to_string(&item).unwrap();
let parsed: Item = from_str(&xml).unwrap();
assert_eq!(parsed, item);
}
#[test]
fn test_round_trip_enum_field_struct_variant() {
#[derive(Debug, Serialize, Deserialize, PartialEq)]
enum Status {
Point { x: i32, y: i32 },
}
#[derive(Debug, Serialize, Deserialize, PartialEq)]
struct Item {
status: Status,
name: String,
}
let item = Item {
status: Status::Point { x: 1, y: 2 },
name: "widget".to_string(),
};
let xml = crate::to_string(&item).unwrap();
let parsed: Item = from_str(&xml).unwrap();
assert_eq!(parsed, item);
}
#[test]
fn test_round_trip_enum_field_struct_variant_text() {
#[derive(Debug, Serialize, Deserialize, PartialEq)]
enum Content {
Message {
#[serde(rename = "@lang")]
lang: String,
#[serde(rename = "$text")]
text: String,
},
}
#[derive(Debug, Serialize, Deserialize, PartialEq)]
struct Doc {
content: Content,
footer: String,
}
let doc = Doc {
content: Content::Message {
lang: "en".to_string(),
text: "hello".to_string(),
},
footer: "bye".to_string(),
};
let xml = crate::to_string(&doc).unwrap();
let parsed: Doc = from_str(&xml).unwrap();
assert_eq!(parsed, doc);
}
#[test]
fn test_deserialize_top_level_enum_variants() {
#[derive(Debug, Serialize, Deserialize, PartialEq)]
enum Value {
Unit,
Count(u32),
Pair(u32, String),
Point { x: i32, y: i32 },
}
let unit: Value = from_str("<Unit/>").unwrap();
assert_eq!(unit, Value::Unit);
let count: Value = from_str("<Count>42</Count>").unwrap();
assert_eq!(count, Value::Count(42));
let pair: Value = from_str("<Pair><item>1</item><item>one</item></Pair>").unwrap();
assert_eq!(pair, Value::Pair(1, "one".to_string()));
let point: Value = from_str("<Point><x>1</x><y>2</y></Point>").unwrap();
assert_eq!(point, Value::Point { x: 1, y: 2 });
}
#[test]
fn test_deserialize_cdata_content() {
#[derive(Debug, Deserialize, PartialEq)]
struct Data {
content: String,
}
let xml = "<Data><content><![CDATA[<raw> & stuff]]></content></Data>";
let data: Data = from_str(xml).unwrap();
assert_eq!(data.content, "<raw> & stuff");
}
#[test]
fn test_from_bytes_invalid_utf8_has_position() {
let bytes = b"<a>\n\xff</a>";
let err = from_bytes::<String>(bytes).unwrap_err();
let pos = err.position().expect("error should carry a position");
assert_eq!(pos.line, 2);
assert_eq!(pos.column, 1);
assert_eq!(pos.offset, 4);
}
#[test]
fn test_deserialize_mixed_text_and_cdata() {
#[derive(Debug, Deserialize, PartialEq)]
struct Data {
content: String,
}
let xml = "<Data><content>a<![CDATA[<b>]]>c</content></Data>";
let data: Data = from_str(xml).unwrap();
assert_eq!(data.content, "a<b>c");
}
#[test]
fn test_deserialize_bytes_borrowed() {
#[derive(Debug, PartialEq)]
struct Bytes<'a>(Cow<'a, [u8]>);
impl<'de: 'a, 'a> Deserialize<'de> for Bytes<'a> {
fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
where
D: de::Deserializer<'de>,
{
struct BytesVisitor;
impl<'de> Visitor<'de> for BytesVisitor {
type Value = Bytes<'de>;
fn expecting(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str("bytes")
}
fn visit_borrowed_bytes<E>(
self,
v: &'de [u8],
) -> std::result::Result<Self::Value, E> {
Ok(Bytes(Cow::Borrowed(v)))
}
fn visit_byte_buf<E>(self, v: Vec<u8>) -> std::result::Result<Self::Value, E> {
Ok(Bytes(Cow::Owned(v)))
}
}
deserializer.deserialize_bytes(BytesVisitor)
}
}
#[derive(Debug, Deserialize, PartialEq)]
struct Doc<'a> {
#[serde(borrow)]
b: Bytes<'a>,
}
let xml = "<D><b>hi</b></D>";
let doc: Doc<'_> = from_str(xml).unwrap();
assert_eq!(doc.b.0.as_ref(), b"hi");
assert!(matches!(doc.b.0, Cow::Borrowed(_)));
let xml = "<D><b>a & b</b></D>";
let doc: Doc<'_> = from_str(xml).unwrap();
assert_eq!(doc.b.0.as_ref(), b"a & b");
assert!(matches!(doc.b.0, Cow::Owned(_)));
}
#[test]
fn test_deserialize_ignores_unknown_nested_element() {
#[derive(Debug, Deserialize, PartialEq)]
struct Person {
name: String,
}
let xml = "<Person><name>Alice</name><extra><deep>x</deep></extra></Person>";
let person: Person = from_str(xml).unwrap();
assert_eq!(person.name, "Alice");
#[derive(Debug, Deserialize, PartialEq)]
struct People {
person: Vec<Person>,
}
let xml = "<People>\
<person><name>Alice</name><extra><deep>x</deep></extra></person>\
<person><name>Bob</name></person>\
</People>";
let people: People = from_str(xml).unwrap();
assert_eq!(
people.person,
vec![
Person {
name: "Alice".to_string()
},
Person {
name: "Bob".to_string()
},
]
);
}
#[test]
fn test_deserialize_empty_input_errors() {
#[derive(Debug, Deserialize, PartialEq)]
struct Person {
name: String,
age: u32,
}
let err = from_str::<Person>("").unwrap_err();
assert!(matches!(
err.kind(),
crate::error::ErrorKind::UnexpectedEof
));
}
#[test]
fn test_deserialize_unit_struct_truncated_errors() {
#[derive(Debug, Deserialize, PartialEq)]
struct Unit;
assert!(from_str::<Unit>("<Unit>").is_err());
}
#[test]
fn test_deserialize_enum_field_empty_element_errors() {
#[derive(Debug, Deserialize, PartialEq)]
enum Status {
Active,
}
#[derive(Debug, Deserialize, PartialEq)]
struct S {
status: Status,
}
let err = from_str::<S>("<S><status/></S>").unwrap_err();
assert!(err.to_string().contains("empty element `<status/>`"));
}
}