Skip to content
Merged
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
59 changes: 58 additions & 1 deletion xmlity-quick-xml/src/de.rs
Original file line number Diff line number Diff line change
Expand Up @@ -355,13 +355,15 @@ impl<'de> de::AttributeAccess<'de> for AttributeAccess<'_, 'de> {
T::deserialize(TextDeserializer {
value: self.value,
deserializer: self.deserializer,
used_up: false,
})
}
}

struct TextDeserializer<'a, 'v> {
value: Cow<'v, [u8]>,
deserializer: &'a Deserializer<'a>,
used_up: bool,
}

impl<'de> de::XmlText<'de> for TextDeserializer<'_, 'de> {
Expand Down Expand Up @@ -394,6 +396,61 @@ impl<'de> de::XmlText<'de> for TextDeserializer<'_, 'de> {
}
}

impl<'de> de::SeqAccess<'de> for TextDeserializer<'_, 'de> {
type Error = Error;

type SubAccess<'g>
= TextDeserializer<'g, 'de>
where
Self: 'g;

fn next_element<T>(&mut self) -> Result<Option<T>, Self::Error>
where
T: Deserialize<'de>,
{
if self.used_up {
return Ok(None);
}

T::deserialize(TextDeserializer {
value: self.value.clone(),
deserializer: self.deserializer,
used_up: false,
})
.map(|value| {
self.used_up = true;
Some(value)
})
}

fn next_element_seq<T>(&mut self) -> Result<Option<T>, Self::Error>
where
T: Deserialize<'de>,
{
if self.used_up {
return Ok(None);
}

T::deserialize_seq(TextDeserializer {
value: self.value.clone(),
deserializer: self.deserializer,
used_up: false,
})
.map(|value| {
self.used_up = true;
Some(value)
})
}

fn sub_access(&mut self) -> Result<Self::SubAccess<'_>, Self::Error> {
Ok(TextDeserializer {
value: self.value.clone(),
deserializer: self.deserializer,
used_up: self.used_up,
})
}
}

impl<'de> de::Deserializer<'de> for TextDeserializer<'_, 'de> {
type Error = Error;

Expand All @@ -408,7 +465,7 @@ impl<'de> de::Deserializer<'de> for TextDeserializer<'_, 'de> {
where
V: Visitor<'de>,
{
visitor.visit_text(self)
visitor.visit_seq(self)
}
}

Expand Down
50 changes: 43 additions & 7 deletions xmlity-quick-xml/src/ser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -348,19 +348,49 @@ pub struct AttributeSerializer<'t, W: Write> {

/// The text serializer for the `quick-xml` crate. Used when serializing to an attribute value.
pub struct TextSerializer {
value: Vec<u8>,
value: Option<String>,
}

impl ser::Serializer for &mut TextSerializer {
impl ser::SerializeSeq for &mut TextSerializer {
type Ok = ();
type Error = Error;

fn serialize_element<V: Serialize>(&mut self, value: &V) -> Result<Self::Ok, Self::Error> {
if self.value.is_some() {
return Err(Error::unexpected_serialize(Unexpected::Text));
}

let mut text_ser = TextSerializer { value: None };
value.serialize(&mut text_ser)?;

if let Some(value) = text_ser.value {
self.value = Some(value);
} else {
return Err(Error::unexpected_serialize(Unexpected::None));
}

Ok(())
}

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

impl<'a> ser::Serializer for &'a mut TextSerializer {
type Ok = ();
type Error = Error;

type SerializeElement = NoopDeSerializer<Self::Ok, Self::Error>;

type SerializeSeq = NoopDeSerializer<Self::Ok, Self::Error>;
type SerializeSeq = &'a mut TextSerializer;

fn serialize_text<S: AsRef<str>>(self, text: S) -> Result<Self::Ok, Self::Error> {
self.value.extend_from_slice(text.as_ref().as_bytes());
if self.value.is_some() {
return Err(Error::unexpected_serialize(Unexpected::Text));
}

self.value = Some(text.as_ref().to_string());

Ok(())
}
Expand All @@ -381,7 +411,7 @@ impl ser::Serializer for &mut TextSerializer {
}

fn serialize_seq(self) -> Result<Self::SerializeSeq, Self::Error> {
Err(Error::unexpected_serialize(Unexpected::Seq))
Ok(self)
}

fn serialize_decl<S: AsRef<str>>(
Expand Down Expand Up @@ -447,11 +477,17 @@ impl<W: Write> ser::SerializeAttributeAccess for AttributeSerializer<'_, W> {
self.serializer.push_decl_attr(decl);
}

let mut text_ser = TextSerializer { value: Vec::new() };
let mut text_ser = TextSerializer { value: None };

value.serialize(&mut text_ser)?;

self.serializer.push_attr(qname, text_ser.value);
self.serializer.push_attr(
qname,
text_ser
.value
.expect("TextSerializer should have a value")
.into_bytes(),
);

Ok(())
}
Expand Down
65 changes: 57 additions & 8 deletions xmlity-quick-xml/tests/elements/inline_attribute_declarations.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,39 @@ use crate::define_test;

use xmlity::{Deserialize, Serialize};

fn f_serialize<T: xmlity::Serializer>(f: &F, serializer: T) -> Result<T::Ok, T::Error> {
serializer.serialize_text(&f.0)
}

fn f_deserialize<'de, T: xmlity::Deserializer<'de>>(deserializer: T) -> Result<F, T::Error> {
let s = String::deserialize(deserializer)?;
Ok(F(s))
}

#[derive(Debug, PartialEq, Serialize, Deserialize)]
#[xvalue(serialize_with = "f_serialize", deserialize_with = "f_deserialize")]
struct F(pub String);

#[derive(Debug, PartialEq, Serialize, Deserialize)]
#[xelement(name = "c")]
pub struct C {
struct C {
#[xattribute(name = "b")]
pub c: String,
pub c: F,
}

define_test!(
element_with_single_child,
[(C { c: "A".to_string() }, r#"<c b="A"/>"#)]
[(
C {
c: F("A".to_string())
},
r#"<c b="A"/>"#
)]
);

#[derive(Debug, PartialEq, Serialize, Deserialize)]
#[xelement(name = "d")]
pub struct D {
struct D {
#[xattribute(name = "b")]
pub b: String,
pub c: C,
Expand All @@ -27,15 +45,17 @@ define_test!(
[(
D {
b: "A".to_string(),
c: C { c: "B".to_string() }
c: C {
c: F("B".to_string())
}
},
r#"<d b="A"><c b="B"/></d>"#
)]
);

#[derive(Debug, PartialEq, Serialize, Deserialize)]
#[xelement(name = "e")]
pub struct E {
struct E {
pub d: Vec<D>,
}

Expand All @@ -46,14 +66,43 @@ define_test!(
d: vec![
D {
b: "A".to_string(),
c: C { c: "B".to_string() }
c: C {
c: F("B".to_string())
}
},
D {
b: "C".to_string(),
c: C { c: "D".to_string() }
c: C {
c: F("D".to_string())
}
}
]
},
r#"<e><d b="A"><c b="B"/></d><d b="C"><c b="D"/></d></e>"#
)]
);

#[derive(Debug, PartialEq, Serialize, Deserialize)]
enum H {
F(F),
}

#[derive(Debug, PartialEq, Serialize, Deserialize)]
#[xelement(name = "g")]
struct G {
#[xattribute(name = "f", optional, default)]
pub f: Option<H>,
}

define_test!(
element_with_optional_attribute_of_enum,
[
(
G {
f: Some(H::F(F("A".to_string())))
},
r#"<g f="A"/>"#
),
(G { f: None }, r#"<g/>"#)
]
);