Skip to content

Commit d18409b

Browse files
KakadusMingun
authored andcommitted
Fix serialization of structs with only text content
1 parent 646ce4b commit d18409b

3 files changed

Lines changed: 134 additions & 23 deletions

File tree

src/de/simple_type.rs

Lines changed: 43 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,13 @@
33
//! [simple types]: https://www.w3schools.com/xml/el_simpletype.asp
44
//! [as defined]: https://www.w3.org/TR/xmlschema11-1/#Simple_Type_Definition
55
6-
use crate::de::Text;
6+
use crate::de::{Text, TEXT_KEY};
77
use crate::encoding::Decoder;
88
use crate::errors::serialize::DeError;
99
use crate::escape::unescape;
1010
use crate::utils::{trim_xml_spaces, CowRef};
1111
use memchr::memchr;
12-
use serde::de::value::UnitDeserializer;
12+
use serde::de::value::{MapDeserializer, SeqAccessDeserializer, UnitDeserializer};
1313
use serde::de::{
1414
DeserializeSeed, Deserializer, EnumAccess, IntoDeserializer, SeqAccess, VariantAccess, Visitor,
1515
};
@@ -477,6 +477,17 @@ impl<'de, 'a> SeqAccess<'de> for ListIter<'de, 'a> {
477477
}
478478
}
479479

480+
/// serde<=1.0.213 does not implement `IntoDeserializer` for `SeqAccessDeserializer`,
481+
/// which is required for `MapDeserializer`, so implement it for our type
482+
impl<'de, 'a> IntoDeserializer<'de, DeError> for ListIter<'de, 'a> {
483+
type Deserializer = SeqAccessDeserializer<Self>;
484+
485+
#[inline]
486+
fn into_deserializer(self) -> Self::Deserializer {
487+
SeqAccessDeserializer::new(self)
488+
}
489+
}
490+
480491
////////////////////////////////////////////////////////////////////////////////////////////////////
481492

482493
/// A deserializer for an xml probably escaped and encoded value of XSD [simple types].
@@ -605,6 +616,19 @@ impl<'de, 'a> SimpleTypeDeserializer<'de, 'a> {
605616
},
606617
})
607618
}
619+
620+
#[inline]
621+
fn as_seq_access(&self) -> Result<ListIter<'de, '_>, DeError> {
622+
let content = match self.decode()? {
623+
CowRef::Input(s) => Content::Input(s),
624+
CowRef::Slice(s) => Content::Slice(s),
625+
CowRef::Owned(s) => Content::Owned(s, 0),
626+
};
627+
Ok(ListIter {
628+
content: Some(content),
629+
escaped: self.escaped,
630+
})
631+
}
608632
}
609633

610634
impl<'de, 'a> Deserializer<'de> for SimpleTypeDeserializer<'de, 'a> {
@@ -685,15 +709,7 @@ impl<'de, 'a> Deserializer<'de> for SimpleTypeDeserializer<'de, 'a> {
685709
where
686710
V: Visitor<'de>,
687711
{
688-
let content = match self.decode()? {
689-
CowRef::Input(s) => Content::Input(s),
690-
CowRef::Slice(s) => Content::Slice(s),
691-
CowRef::Owned(s) => Content::Owned(s, 0),
692-
};
693-
visitor.visit_seq(ListIter {
694-
content: Some(content),
695-
escaped: self.escaped,
696-
})
712+
visitor.visit_seq(self.as_seq_access()?)
697713
}
698714

699715
/// Representation of tuples the same as [sequences][Self::deserialize_seq].
@@ -720,7 +736,22 @@ impl<'de, 'a> Deserializer<'de> for SimpleTypeDeserializer<'de, 'a> {
720736
}
721737

722738
unsupported!(deserialize_map);
723-
unsupported!(deserialize_struct(&'static str, &'static [&'static str]));
739+
740+
fn deserialize_struct<V>(
741+
self,
742+
_name: &'static str,
743+
fields: &'static [&'static str],
744+
visitor: V,
745+
) -> Result<V::Value, Self::Error>
746+
where
747+
V: Visitor<'de>,
748+
{
749+
if fields == [TEXT_KEY] {
750+
let seq = self.as_seq_access()?;
751+
return visitor.visit_map(MapDeserializer::new(std::iter::once((TEXT_KEY, seq))));
752+
}
753+
self.deserialize_str(visitor)
754+
}
724755

725756
fn deserialize_enum<V>(
726757
self,

src/se/simple_type.rs

Lines changed: 44 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,13 @@
33
//! [simple types]: https://www.w3schools.com/xml/el_simpletype.asp
44
//! [as defined]: https://www.w3.org/TR/xmlschema11-1/#Simple_Type_Definition
55
6+
use crate::de::TEXT_KEY;
67
use crate::escape::escape_char;
8+
use crate::se::text::TextSerializer;
79
use crate::se::{QuoteLevel, SeError};
810
use crate::utils::CDataIterator;
911
use serde::ser::{
10-
Impossible, Serialize, SerializeSeq, SerializeTuple, SerializeTupleStruct,
12+
Impossible, Serialize, SerializeSeq, SerializeStruct, SerializeTuple, SerializeTupleStruct,
1113
SerializeTupleVariant, Serializer,
1214
};
1315
use std::fmt::{self, Write};
@@ -480,7 +482,7 @@ impl<W: Write> Serializer for SimpleTypeSerializer<W> {
480482
type SerializeTupleStruct = SimpleSeq<W>;
481483
type SerializeTupleVariant = Impossible<Self::Ok, Self::Error>;
482484
type SerializeMap = Impossible<Self::Ok, Self::Error>;
483-
type SerializeStruct = Impossible<Self::Ok, Self::Error>;
485+
type SerializeStruct = SimpleSeq<W>;
484486
type SerializeStructVariant = Impossible<Self::Ok, Self::Error>;
485487

486488
write_primitive!();
@@ -561,18 +563,18 @@ impl<W: Write> Serializer for SimpleTypeSerializer<W> {
561563
))
562564
}
563565

566+
#[inline]
564567
fn serialize_struct(
565568
self,
566-
name: &'static str,
569+
_name: &'static str,
567570
_len: usize,
568571
) -> Result<Self::SerializeStruct, Self::Error> {
569-
Err(SeError::Unsupported(
570-
format!(
571-
"cannot serialize struct `{}` as an attribute or text content value",
572-
name
573-
)
574-
.into(),
575-
))
572+
Ok(SimpleSeq {
573+
writer: self.writer,
574+
target: self.target,
575+
level: self.level,
576+
is_empty: true,
577+
})
576578
}
577579

578580
fn serialize_struct_variant(
@@ -679,6 +681,37 @@ impl<W: Write> SerializeTupleVariant for SimpleSeq<W> {
679681
}
680682
}
681683

684+
impl<W: Write> SerializeStruct for SimpleSeq<W> {
685+
type Ok = W;
686+
type Error = SeError;
687+
688+
fn serialize_field<T: ?Sized + Serialize>(
689+
&mut self,
690+
key: &'static str,
691+
value: &T,
692+
) -> Result<(), Self::Error> {
693+
if self.is_empty && key == TEXT_KEY {
694+
let ser = TextSerializer(SimpleTypeSerializer {
695+
writer: &mut self.writer,
696+
target: self.target,
697+
level: self.level,
698+
});
699+
value.serialize(ser)?;
700+
self.is_empty = false;
701+
Ok(())
702+
} else {
703+
Err(SeError::Unsupported(
704+
format!("only struct which contains exactly one `{TEXT_KEY}` field may be serialized as an attribute or text content value").into(),
705+
))
706+
}
707+
}
708+
709+
#[inline]
710+
fn end(self) -> Result<Self::Ok, Self::Error> {
711+
SerializeSeq::end(self)
712+
}
713+
}
714+
682715
////////////////////////////////////////////////////////////////////////////////////////////////////
683716

684717
#[cfg(test)]
@@ -1260,7 +1293,7 @@ mod tests {
12601293
err!(map: BTreeMap::from([(1, 2), (3, 4)])
12611294
=> Unsupported("cannot serialize map as an attribute or text content value"));
12621295
err!(struct_: Struct { key: "answer", val: 42 }
1263-
=> Unsupported("cannot serialize struct `Struct` as an attribute or text content value"));
1296+
=> Unsupported("only struct which contains exactly one `$text` field may be serialized as an attribute or text content value"));
12641297
err!(enum_struct: Enum::Struct { key: "answer", val: 42 }
12651298
=> Unsupported("cannot serialize enum struct variant `Enum::Struct` as an attribute or text content value"));
12661299
}

tests/serde-issues.rs

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -709,3 +709,50 @@ fn issue888() {
709709
}
710710
);
711711
}
712+
713+
/// Regression test for https://github.com/tafia/quick-xml/issues/906.
714+
#[test]
715+
fn issue906() {
716+
#[derive(Debug, PartialEq, Deserialize, Serialize)]
717+
struct AsElement {
718+
#[serde(rename = "a-list")]
719+
a_list: TextContent,
720+
}
721+
722+
#[derive(Debug, PartialEq, Deserialize, Serialize)]
723+
struct AsAttribute {
724+
#[serde(rename = "@a-list")]
725+
a_list: TextContent,
726+
}
727+
728+
#[derive(Debug, PartialEq, Deserialize, Serialize)]
729+
struct TextContent {
730+
#[serde(rename = "$text")]
731+
content: Vec<String>,
732+
#[serde(skip)]
733+
ignored: (),
734+
}
735+
736+
let foo = AsElement {
737+
a_list: TextContent {
738+
content: vec!["A".to_string(), "B".to_string()],
739+
ignored: (),
740+
},
741+
};
742+
let bar = AsAttribute {
743+
a_list: TextContent {
744+
content: vec!["A".to_string(), "B".to_string()],
745+
ignored: (),
746+
},
747+
};
748+
749+
let buffer = to_string_with_root("test", &foo).unwrap();
750+
assert_eq!(buffer, "<test><a-list>A B</a-list></test>");
751+
let foo2: AsElement = from_str(&buffer).unwrap();
752+
assert_eq!(foo2, foo);
753+
754+
let buffer = to_string_with_root("test", &bar).unwrap();
755+
assert_eq!(buffer, "<test a-list=\"A B\"/>");
756+
let bar2: AsAttribute = from_str(&buffer).unwrap();
757+
assert_eq!(bar2, bar);
758+
}

0 commit comments

Comments
 (0)