From 89f2483dac30936b9ce37c5fc212ff4dec92dff1 Mon Sep 17 00:00:00 2001 From: Lennart <18233294+lennart-k@users.noreply.github.com> Date: Wed, 5 Feb 2025 17:56:52 +0100 Subject: [PATCH] xml: Support CDATA --- crates/xml/derive/src/xml_struct.rs | 8 +++++--- crates/xml/src/error.rs | 4 ++++ crates/xml/src/value.rs | 8 ++++++++ crates/xml/tests/de_struct.rs | 28 ++++++++++++++++++++++++++++ 4 files changed, 45 insertions(+), 3 deletions(-) diff --git a/crates/xml/derive/src/xml_struct.rs b/crates/xml/derive/src/xml_struct.rs index 6b77735..6f3e964 100644 --- a/crates/xml/derive/src/xml_struct.rs +++ b/crates/xml/derive/src/xml_struct.rs @@ -111,7 +111,8 @@ impl NamedStruct { if untagged_field_branches.len() > 1 { panic!("Currently only one untagged field supported!"); } - let text_field_branches = self.fields.iter().filter_map(Field::text_branch); + let text_field_branches: Vec<_> = + self.fields.iter().filter_map(Field::text_branch).collect(); let attr_field_branches = self.fields.iter().filter_map(Field::attr_branch); let tagname_field_branches = self.fields.iter().filter_map(Field::tagname_branch); @@ -175,7 +176,8 @@ impl NamedStruct { #(#text_field_branches)* } Event::CData(cdata) => { - return Err(XmlError::UnsupportedEvent("CDATA")); + let text = String::from_utf8(cdata.to_vec())?; + #(#text_field_branches)* } Event::Comment(_) => { /* ignore */ } Event::Decl(_) => { @@ -191,7 +193,7 @@ impl NamedStruct { return Err(XmlError::UnsupportedEvent("Doctype in the middle of the document")); } Event::End(end) => { - // Error: premature end + // This should actually be unreachable return Err(XmlError::Other("Unexpected closing tag for wrong element".to_owned())); } } diff --git a/crates/xml/src/error.rs b/crates/xml/src/error.rs index 36db07d..76c7008 100644 --- a/crates/xml/src/error.rs +++ b/crates/xml/src/error.rs @@ -1,3 +1,5 @@ +use std::string::FromUtf8Error; + use thiserror::Error; #[derive(Debug, Error)] @@ -6,6 +8,8 @@ pub enum XmlError { QuickXmlError(#[from] quick_xml::Error), #[error(transparent)] QuickXmlAttrError(#[from] quick_xml::events::attributes::AttrError), + #[error(transparent)] + FromUtf8Error(#[from] FromUtf8Error), #[error("Invalid tag [{0}]{1}. Expected [{2}]{3}")] InvalidTag(String, String, String, String), #[error("Missing field {0}")] diff --git a/crates/xml/src/value.rs b/crates/xml/src/value.rs index 6363c9f..3636da8 100644 --- a/crates/xml/src/value.rs +++ b/crates/xml/src/value.rs @@ -81,6 +81,14 @@ impl XmlDeserialize for T { } string = String::from_utf8_lossy(text.as_ref()).to_string(); } + Event::CData(cdata) => { + let text = String::from_utf8(cdata.to_vec())?; + if !string.is_empty() { + // Content already written + return Err(XmlError::UnsupportedEvent("content already written")); + } + string = text; + } Event::End(_) => break, Event::Eof => return Err(XmlError::Eof), _ => return Err(XmlError::UnsupportedEvent("todo")), diff --git a/crates/xml/tests/de_struct.rs b/crates/xml/tests/de_struct.rs index f1cbaeb..d281c62 100644 --- a/crates/xml/tests/de_struct.rs +++ b/crates/xml/tests/de_struct.rs @@ -257,6 +257,34 @@ fn test_xml_values() { ); } +#[test] +fn test_xml_cdata() { + #[derive(XmlDeserialize, XmlRootTag, PartialEq, Debug)] + #[xml(root = b"document")] + struct Document { + #[xml(ty = "text")] + hello: String, + href: String, + } + + let doc = Document::parse_str( + r#" + + + + + "#, + ) + .unwrap(); + assert_eq!( + doc, + Document { + hello: "some text".to_owned(), + href: "some stuff".to_owned() + } + ); +} + #[test] fn test_struct_xml_decl() { #[derive(Debug, XmlDeserialize, XmlRootTag, PartialEq)]