Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 29 additions & 3 deletions src/de/simple_type.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,13 @@
//! [simple types]: https://www.w3schools.com/xml/el_simpletype.asp
//! [as defined]: https://www.w3.org/TR/xmlschema11-1/#Simple_Type_Definition

use crate::de::Text;
use crate::de::{Text, TEXT_KEY};
use crate::encoding::Decoder;
use crate::errors::serialize::DeError;
use crate::escape::unescape;
use crate::utils::CowRef;
use memchr::memchr;
use serde::de::value::UnitDeserializer;
use serde::de::value::{MapDeserializer, SeqAccessDeserializer, UnitDeserializer};
use serde::de::{
DeserializeSeed, Deserializer, EnumAccess, IntoDeserializer, SeqAccess, VariantAccess, Visitor,
};
Expand Down Expand Up @@ -727,7 +727,6 @@ impl<'de, 'a> Deserializer<'de> for SimpleTypeDeserializer<'de, 'a> {
}

unsupported!(deserialize_map);
unsupported!(deserialize_struct(&'static str, &'static [&'static str]));

fn deserialize_enum<V>(
self,
Expand Down Expand Up @@ -757,6 +756,33 @@ impl<'de, 'a> Deserializer<'de> for SimpleTypeDeserializer<'de, 'a> {
{
visitor.visit_unit()
}

fn deserialize_struct<V>(
self,
name: &'static str,
fields: &'static [&'static str],
visitor: V,
) -> Result<V::Value, Self::Error>
where
V: Visitor<'de>,
{
if fields == [TEXT_KEY] {
let content = match self.decode()? {
CowRef::Input(s) => Content::Input(s),
CowRef::Slice(s) => Content::Slice(s),
CowRef::Owned(s) => Content::Owned(s, 0),
};
let list_iter = ListIter {
content: Some(content),
escaped: self.escaped,
};

let seq_deserializer = SeqAccessDeserializer::new(list_iter);
let der = MapDeserializer::new(std::iter::once((TEXT_KEY, seq_deserializer)));
return der.deserialize_map(visitor);
}
self.deserialize_str(visitor)
}
}

impl<'de, 'a> EnumAccess<'de> for SimpleTypeDeserializer<'de, 'a> {
Expand Down
50 changes: 45 additions & 5 deletions src/se/simple_type.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,13 @@
//! [simple types]: https://www.w3schools.com/xml/el_simpletype.asp
//! [as defined]: https://www.w3.org/TR/xmlschema11-1/#Simple_Type_Definition

use crate::de::TEXT_KEY;
use crate::escape::escape_char;
use crate::se::text::TextSerializer;
use crate::se::{QuoteLevel, SeError};
use crate::utils::CDataIterator;
use serde::ser::{
Impossible, Serialize, SerializeSeq, SerializeTuple, SerializeTupleStruct,
Impossible, Serialize, SerializeSeq, SerializeStruct, SerializeTuple, SerializeTupleStruct,
SerializeTupleVariant, Serializer,
};
use serde::serde_if_integer128;
Expand Down Expand Up @@ -473,6 +475,34 @@ impl<W: Write> SimpleTypeSerializer<W> {
Ok(self.writer.write_fmt(args)?)
}
}
impl<'w, W: Write> SerializeStruct for SimpleSeq<W> {
type Ok = W;
type Error = SeError;

fn serialize_field<T: ?Sized + Serialize>(
&mut self,
key: &'static str,
value: &T,
) -> Result<(), Self::Error> {
if key == TEXT_KEY {
let ser = TextSerializer(SimpleTypeSerializer {
writer: &mut self.writer,
target: self.target,
level: self.level,
});
value.serialize(ser)?;
Ok(())
} else {
Err(SeError::Unsupported(
format!("only `{TEXT_KEY}` field is supported in text content").into(),
))
}
}

fn end(self) -> Result<Self::Ok, Self::Error> {
SerializeSeq::end(self)
}
}

impl<W: Write> Serializer for SimpleTypeSerializer<W> {
type Ok = W;
Expand All @@ -483,7 +513,7 @@ impl<W: Write> Serializer for SimpleTypeSerializer<W> {
type SerializeTupleStruct = SimpleSeq<W>;
type SerializeTupleVariant = Impossible<Self::Ok, Self::Error>;
type SerializeMap = Impossible<Self::Ok, Self::Error>;
type SerializeStruct = Impossible<Self::Ok, Self::Error>;
type SerializeStruct = SimpleSeq<W>;
type SerializeStructVariant = Impossible<Self::Ok, Self::Error>;

write_primitive!();
Expand Down Expand Up @@ -564,14 +594,24 @@ impl<W: Write> Serializer for SimpleTypeSerializer<W> {
))
}

#[inline]
fn serialize_struct(
self,
name: &'static str,
_len: usize,
len: usize,
) -> Result<Self::SerializeStruct, Self::Error> {
if len == 1 {
let seq = SimpleSeq {
writer: self.writer,
target: self.target,
level: self.level,
is_empty: true,
};
return Ok(seq);
}
Err(SeError::Unsupported(
format!(
"cannot serialize struct `{}` as an attribute or text content value",
"cannot serialize struct `{}` having more than one field as an attribute or text content value",
name
)
.into(),
Expand Down Expand Up @@ -1267,7 +1307,7 @@ mod tests {
err!(map: BTreeMap::from([(1, 2), (3, 4)])
=> Unsupported("cannot serialize map as an attribute or text content value"));
err!(struct_: Struct { key: "answer", val: 42 }
=> Unsupported("cannot serialize struct `Struct` as an attribute or text content value"));
=> Unsupported("cannot serialize struct `Struct` having more than one field as an attribute or text content value"));
err!(enum_struct: Enum::Struct { key: "answer", val: 42 }
=> Unsupported("cannot serialize enum struct variant `Enum::Struct` as an attribute or text content value"));
}
Expand Down
43 changes: 43 additions & 0 deletions tests/serde-issues.rs
Original file line number Diff line number Diff line change
Expand Up @@ -709,3 +709,46 @@ fn issue888() {
}
);
}

/// Regression test for https://github.com/tafia/quick-xml/issues/906.
#[test]
fn issue906() {
#[derive(Debug, PartialEq, Deserialize, Serialize)]
struct AsElement {
#[serde(rename = "a-list")]
a_list: TextContent,
}

#[derive(Debug, PartialEq, Deserialize, Serialize)]
struct AsAttribute {
#[serde(rename = "@a-list")]
a_list: TextContent,
}

#[derive(Debug, PartialEq, Deserialize, Serialize)]
pub struct TextContent {
#[serde(default, rename = "$text")]
content: Vec<String>,
}
let foo = AsElement {
a_list: TextContent {
content: vec!["A".to_string(), "B".to_string()],
},
};
let bar = AsAttribute {
a_list: TextContent {
content: vec!["A".to_string(), "B".to_string()],
},
};

let buffer = to_string_with_root("test", &foo).unwrap();
std::assert_eq!(buffer, "<test><a-list>A B</a-list></test>");
let foo2: AsElement = from_str(&buffer).unwrap();
std::assert_eq!(foo2, foo);

let buffer = to_string_with_root("test", &bar).unwrap();
std::assert_eq!(buffer, "<test a-list=\"A B\"/>");

let bar2: AsAttribute = from_str(&buffer).unwrap();
std::assert_eq!(bar2, bar);
}
Loading