diff --git a/xsd-parser-types/src/xml/element.rs b/xsd-parser-types/src/xml/element.rs index a6143397..e35025a8 100644 --- a/xsd-parser-types/src/xml/element.rs +++ b/xsd-parser-types/src/xml/element.rs @@ -10,7 +10,7 @@ use quick_xml::{ name::QName, }; -use crate::misc::format_utf8_slice; +use crate::misc::{format_utf8_slice, Namespace, NamespacePrefix}; #[cfg(feature = "quick-xml")] use crate::quick_xml::{ @@ -93,6 +93,29 @@ impl<'a> Element<'a> { self } + + /// Add a namespace to the namespace context of this element. + /// + /// This will not add a namespace attribute to the element itself. It only + /// tells the serializer that this namespace must be valid in the context of + /// this element. If the namespace is not already declared in a parent element, + /// a suitable `xmlns` attribute will be added automatically. + /// + /// If you want to add a namespace declaration attribute to the element in + /// any case, use the [`Element::attribute`] method instead. + #[must_use] + pub fn namespace(mut self, prefix: P, namespace: N) -> Self + where + P: Into>, + N: Into>, + { + let mut namespaces = self.namespaces.into_owned(); + namespaces.insert(prefix.into().into(), namespace.into().into()); + + self.namespaces = namespaces.into_shared(); + + self + } } impl Debug for Element<'_> { @@ -172,6 +195,7 @@ impl<'ser, 'el> ElementSerializer<'ser, 'el> { Self::Start { name, element } } + #[allow(clippy::too_many_lines)] fn next_item(&mut self, helper: &mut SerializeHelper) -> Result>, Error> { loop { match replace(self, Self::Done) { @@ -183,9 +207,19 @@ impl<'ser, 'el> ElementSerializer<'ser, 'el> { }); let mut start = BytesStart::new(element_name); + + helper.begin_ns_scope(); + for (prefix, ns) in &**element.namespaces { + let prefix = NamespacePrefix::new(prefix.0.clone().into_owned()); + let ns = Namespace::new(ns.0.clone().into_owned()); + helper.write_xmlns(&mut start, Some(&prefix), &ns); + } + start.extend_attributes(attributes); let event = if element.values.is_empty() { + helper.end_ns_scope(); + Event::Empty(start) } else { let values = element.values.iter(); @@ -206,6 +240,8 @@ impl<'ser, 'el> ElementSerializer<'ser, 'el> { let end = BytesEnd::new(element_name); let event = Event::End(end); + helper.end_ns_scope(); + return Ok(Some(event)); } Self::NextValue { diff --git a/xsd-parser-types/src/xml/namespaces.rs b/xsd-parser-types/src/xml/namespaces.rs index a0663c5f..f651a298 100644 --- a/xsd-parser-types/src/xml/namespaces.rs +++ b/xsd-parser-types/src/xml/namespaces.rs @@ -12,12 +12,18 @@ use crate::misc::format_utf8_slice; #[derive(Default, Debug, Clone, Eq, PartialEq)] pub struct Namespaces<'a>(pub HashMap, Value<'a>>); -impl Namespaces<'_> { +impl<'a> Namespaces<'a> { /// Create a new [`Namespaces`] instance. #[must_use] pub fn new() -> Self { Self::default() } + + /// Convert this list into a shared version ([`NamespacesShared`]). + #[must_use] + pub fn into_shared(self) -> NamespacesShared<'a> { + NamespacesShared::new(self) + } } impl<'a> Deref for Namespaces<'a> { @@ -81,6 +87,12 @@ impl<'a> From<&'a [u8]> for Key<'a> { } } +impl<'a> From> for Key<'a> { + fn from(value: Cow<'a, [u8]>) -> Self { + Self(value) + } +} + impl Deref for Key<'_> { type Target = [u8]; @@ -146,6 +158,24 @@ impl Borrow<[u8]> for Value<'_> { } } +impl From> for Value<'static> { + fn from(value: Vec) -> Self { + Self(Cow::Owned(value)) + } +} + +impl<'a> From<&'a [u8]> for Value<'a> { + fn from(value: &'a [u8]) -> Self { + Self(Cow::Borrowed(value)) + } +} + +impl<'a> From> for Value<'a> { + fn from(value: Cow<'a, [u8]>) -> Self { + Self(value) + } +} + /// Represents a shared list of namespaces. /// /// This is useful to not store the same map again and again. Is uses an [`Arc`] diff --git a/xsd-parser/tests/feature/any/example/default.xml b/xsd-parser/tests/feature/any/example/default.xml index d0a6d14e..9af45ddc 100644 --- a/xsd-parser/tests/feature/any/example/default.xml +++ b/xsd-parser/tests/feature/any/example/default.xml @@ -6,7 +6,7 @@ def - + diff --git a/xsd-parser/tests/feature/any/mod.rs b/xsd-parser/tests/feature/any/mod.rs index e8d5072d..966e2ecf 100644 --- a/xsd-parser/tests/feature/any/mod.rs +++ b/xsd-parser/tests/feature/any/mod.rs @@ -80,7 +80,12 @@ macro_rules! test_obj { }, ChoiceType { any_attribute: AnyAttributes::default(), - content: ChoiceTypeContent::Any(Element::default().name(b"AnyElement2")), + content: ChoiceTypeContent::Any( + Element::default() + .name(b"AnyElement2") + .namespace(b"tns", b"http://example.com") + .namespace(b"anyNs", b"http://example.com/anyNs"), + ), }, ChoiceType { any_attribute: AnyAttributes::default(),