facet_xml/
serialize.rs

1//! XML serialization implementation.
2
3use std::collections::HashMap;
4use std::io::Write;
5
6use base64::Engine;
7use base64::engine::general_purpose::STANDARD as BASE64_STANDARD;
8use facet_core::{Def, Facet, Field, Shape, StructKind};
9use facet_reflect::{HasFields, Peek, is_spanned_shape};
10
11use crate::annotation::{XmlAnnotationPhase, fields_missing_xml_annotations};
12use crate::deserialize::{XmlFieldExt, XmlShapeExt};
13use crate::error::{MissingAnnotationPhase, XmlError, XmlErrorKind};
14
15/// A function that formats a floating-point number to a writer.
16///
17/// This is used to customize how `f32` and `f64` values are serialized to XML.
18/// The function receives the value (as `f64`, with `f32` values upcast) and
19/// a writer to write the formatted output to.
20pub type FloatFormatter = fn(f64, &mut dyn Write) -> std::io::Result<()>;
21
22/// Options for XML serialization.
23#[derive(Clone)]
24pub struct SerializeOptions {
25    /// Whether to pretty-print with indentation (default: false)
26    pub pretty: bool,
27    /// Indentation string for pretty-printing (default: "  ")
28    pub indent: &'static str,
29    /// Custom formatter for floating-point numbers (f32 and f64).
30    /// If `None`, uses the default `Display` implementation.
31    pub float_formatter: Option<FloatFormatter>,
32    /// Whether to preserve entity references (like `&sup1;`, `&#92;`, `&#x5C;`) in string values.
33    ///
34    /// When `true`, entity references in strings are not escaped - the `&` in entity references
35    /// is left as-is instead of being escaped to `&amp;`. This is useful when serializing
36    /// content that already contains entity references (like HTML entities in SVG).
37    ///
38    /// Default: `false` (all `&` characters are escaped to `&amp;`).
39    ///
40    /// # Example
41    ///
42    /// ```
43    /// # use facet::Facet;
44    /// # use facet_xml as xml;
45    /// # use facet_xml::{to_string_with_options, SerializeOptions};
46    ///
47    /// #[derive(Facet)]
48    /// struct Text {
49    ///     #[facet(xml::attribute)]
50    ///     content: String,
51    /// }
52    ///
53    /// let text = Text { content: ".end&sup1;".to_string() };
54    ///
55    /// // Without preserve_entities: &sup1; becomes &amp;sup1;
56    /// let xml = xml::to_string(&text).unwrap();
57    /// assert!(xml.contains("&amp;sup1;"));
58    ///
59    /// // With preserve_entities: &sup1; is preserved
60    /// let options = SerializeOptions::new().preserve_entities(true);
61    /// let xml = to_string_with_options(&text, &options).unwrap();
62    /// assert!(xml.contains("&sup1;"));
63    /// ```
64    pub preserve_entities: bool,
65}
66
67impl Default for SerializeOptions {
68    fn default() -> Self {
69        Self {
70            pretty: false,
71            indent: "  ",
72            float_formatter: None,
73            preserve_entities: false,
74        }
75    }
76}
77
78impl std::fmt::Debug for SerializeOptions {
79    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
80        f.debug_struct("SerializeOptions")
81            .field("pretty", &self.pretty)
82            .field("indent", &self.indent)
83            .field("float_formatter", &self.float_formatter.map(|_| "..."))
84            .field("preserve_entities", &self.preserve_entities)
85            .finish()
86    }
87}
88
89impl SerializeOptions {
90    /// Create new default options (compact output).
91    pub fn new() -> Self {
92        Self::default()
93    }
94
95    /// Enable pretty-printing with default indentation.
96    pub fn pretty(mut self) -> Self {
97        self.pretty = true;
98        self
99    }
100
101    /// Set a custom indentation string (implies pretty-printing).
102    pub fn indent(mut self, indent: &'static str) -> Self {
103        self.indent = indent;
104        self.pretty = true;
105        self
106    }
107
108    /// Get the indent string if pretty-printing is enabled, otherwise None.
109    fn indent_str(&self) -> Option<&str> {
110        if self.pretty { Some(self.indent) } else { None }
111    }
112
113    /// Set a custom formatter for floating-point numbers (f32 and f64).
114    ///
115    /// The formatter function receives the value as `f64` (f32 values are upcast)
116    /// and writes the formatted output to the provided writer.
117    ///
118    /// # Example
119    ///
120    /// ```
121    /// # use facet::Facet;
122    /// # use facet_xml as xml;
123    /// # use facet_xml::{to_string_with_options, SerializeOptions};
124    /// # use std::io::Write;
125    /// fn fmt_g(value: f64, w: &mut dyn Write) -> std::io::Result<()> {
126    ///     // Format like C's %g: 6 significant digits, trim trailing zeros
127    ///     let s = format!("{:.6}", value);
128    ///     let s = s.trim_end_matches('0').trim_end_matches('.');
129    ///     write!(w, "{}", s)
130    /// }
131    ///
132    /// #[derive(Facet)]
133    /// struct Point {
134    ///     #[facet(xml::attribute)]
135    ///     x: f64,
136    ///     #[facet(xml::attribute)]
137    ///     y: f64,
138    /// }
139    ///
140    /// let point = Point { x: 1.5, y: 2.0 };
141    /// let options = SerializeOptions::new().float_formatter(fmt_g);
142    /// let xml = to_string_with_options(&point, &options).unwrap();
143    /// assert_eq!(xml, r#"<Point x="1.5" y="2"/>"#);
144    /// ```
145    pub fn float_formatter(mut self, formatter: FloatFormatter) -> Self {
146        self.float_formatter = Some(formatter);
147        self
148    }
149
150    /// Enable preservation of entity references in string values.
151    ///
152    /// When enabled, entity references like `&sup1;`, `&#92;`, `&#x5C;` are not escaped.
153    /// The `&` in recognized entity patterns is left as-is instead of being escaped to `&amp;`.
154    ///
155    /// This is useful when serializing content that already contains entity references,
156    /// such as HTML entities in SVG content.
157    ///
158    /// # Example
159    ///
160    /// ```
161    /// # use facet::Facet;
162    /// # use facet_xml as xml;
163    /// # use facet_xml::{to_string_with_options, SerializeOptions};
164    ///
165    /// #[derive(Facet)]
166    /// struct Text {
167    ///     #[facet(xml::attribute)]
168    ///     content: String,
169    /// }
170    ///
171    /// let text = Text { content: ".end&sup1;".to_string() };
172    /// let options = SerializeOptions::new().preserve_entities(true);
173    /// let xml = to_string_with_options(&text, &options).unwrap();
174    /// assert!(xml.contains("&sup1;"));
175    /// ```
176    pub fn preserve_entities(mut self, preserve: bool) -> Self {
177        self.preserve_entities = preserve;
178        self
179    }
180}
181
182/// Well-known XML namespace URIs and their conventional prefixes.
183const WELL_KNOWN_NAMESPACES: &[(&str, &str)] = &[
184    ("http://www.w3.org/2001/XMLSchema-instance", "xsi"),
185    ("http://www.w3.org/2001/XMLSchema", "xs"),
186    ("http://www.w3.org/XML/1998/namespace", "xml"),
187    ("http://www.w3.org/1999/xlink", "xlink"),
188    ("http://www.w3.org/2000/svg", "svg"),
189    ("http://www.w3.org/1999/xhtml", "xhtml"),
190    ("http://schemas.xmlsoap.org/soap/envelope/", "soap"),
191    ("http://www.w3.org/2003/05/soap-envelope", "soap12"),
192    ("http://schemas.android.com/apk/res/android", "android"),
193];
194
195pub(crate) type Result<T> = std::result::Result<T, XmlError>;
196
197/// Serialize a value of type `T` to an XML string.
198///
199/// The type `T` must be a struct where fields are marked with XML attributes like
200/// `#[facet(xml::element)]`, `#[facet(xml::attribute)]`, or `#[facet(xml::text)]`.
201///
202/// # Example
203/// ```
204/// # use facet::Facet;
205/// # use facet_xml as xml;
206/// # use facet_xml::to_string;
207/// #[derive(Facet)]
208/// struct Person {
209///     #[facet(xml::attribute)]
210///     id: u32,
211///     #[facet(xml::element)]
212///     name: String,
213/// }
214///
215/// # fn main() -> Result<(), facet_xml::XmlError> {
216/// let person = Person { id: 42, name: "Alice".into() };
217/// let xml = to_string(&person)?;
218/// assert_eq!(xml, r#"<Person id="42"><name>Alice</name></Person>"#);
219/// # Ok(())
220/// # }
221/// ```
222pub fn to_string<T: Facet<'static>>(value: &T) -> Result<String> {
223    to_string_with_options(value, &SerializeOptions::default())
224}
225
226/// Serialize a value of type `T` to a pretty-printed XML string.
227///
228/// This is a convenience function that enables pretty-printing with default indentation.
229///
230/// # Example
231/// ```
232/// # use facet::Facet;
233/// # use facet_xml as xml;
234/// # use facet_xml::to_string_pretty;
235/// #[derive(Facet)]
236/// struct Person {
237///     #[facet(xml::attribute)]
238///     id: u32,
239///     #[facet(xml::element)]
240///     name: String,
241/// }
242///
243/// # fn main() -> Result<(), facet_xml::XmlError> {
244/// let person = Person { id: 42, name: "Alice".into() };
245/// let xml = to_string_pretty(&person)?;
246/// // Output will have newlines and indentation
247/// # Ok(())
248/// # }
249/// ```
250pub fn to_string_pretty<T: Facet<'static>>(value: &T) -> Result<String> {
251    to_string_with_options(value, &SerializeOptions::default().pretty())
252}
253
254/// Serialize a value of type `T` to an XML string with custom options.
255///
256/// # Example
257///
258/// ```
259/// # use facet::Facet;
260/// # use facet_xml as xml;
261/// # use facet_xml::{to_string_with_options, SerializeOptions};
262/// #[derive(Facet)]
263/// struct Person {
264///     #[facet(xml::attribute)]
265///     id: u32,
266///     #[facet(xml::element)]
267///     name: String,
268/// }
269///
270/// # fn main() -> Result<(), facet_xml::XmlError> {
271/// let person = Person { id: 42, name: "Alice".into() };
272///
273/// // Compact output
274/// let xml = to_string_with_options(&person, &SerializeOptions::default())?;
275/// assert_eq!(xml, r#"<Person id="42"><name>Alice</name></Person>"#);
276///
277/// // Pretty output with tabs
278/// let xml = to_string_with_options(&person, &SerializeOptions::default().indent("\t"))?;
279/// # Ok(())
280/// # }
281/// ```
282pub fn to_string_with_options<T: Facet<'static>>(
283    value: &T,
284    options: &SerializeOptions,
285) -> Result<String> {
286    let mut output = Vec::new();
287    to_writer_with_options(&mut output, value, options)?;
288    Ok(String::from_utf8(output).expect("XML output should be valid UTF-8"))
289}
290
291/// Serialize a value of type `T` to a writer as XML.
292///
293/// This is the streaming version of [`to_string`] - it writes directly to any
294/// type implementing [`std::io::Write`].
295///
296/// # Example
297///
298/// Writing to a `Vec<u8>` buffer:
299/// ```
300/// # use facet::Facet;
301/// # use facet_xml as xml;
302/// # use facet_xml::to_writer;
303/// #[derive(Facet)]
304/// struct Person {
305///     #[facet(xml::attribute)]
306///     id: u32,
307///     #[facet(xml::element)]
308///     name: String,
309/// }
310///
311/// # fn main() -> Result<(), facet_xml::XmlError> {
312/// let person = Person { id: 42, name: "Alice".into() };
313/// let mut buffer = Vec::new();
314/// to_writer(&mut buffer, &person)?;
315/// let xml = String::from_utf8(buffer).unwrap();
316/// assert_eq!(xml, r#"<Person id="42"><name>Alice</name></Person>"#);
317/// # Ok(())
318/// # }
319/// ```
320pub fn to_writer<W: Write, T: Facet<'static>>(writer: &mut W, value: &T) -> Result<()> {
321    to_writer_with_options(writer, value, &SerializeOptions::default())
322}
323
324/// Serialize a value of type `T` to a writer as pretty-printed XML.
325///
326/// This is a convenience function that enables pretty-printing with default indentation.
327///
328/// # Example
329///
330/// ```
331/// # use facet::Facet;
332/// # use facet_xml as xml;
333/// # use facet_xml::to_writer_pretty;
334/// #[derive(Facet)]
335/// struct Person {
336///     #[facet(xml::attribute)]
337///     id: u32,
338///     #[facet(xml::element)]
339///     name: String,
340/// }
341///
342/// # fn main() -> Result<(), facet_xml::XmlError> {
343/// let person = Person { id: 42, name: "Alice".into() };
344/// let mut buffer = Vec::new();
345/// to_writer_pretty(&mut buffer, &person)?;
346/// // Output will have newlines and indentation
347/// # Ok(())
348/// # }
349/// ```
350pub fn to_writer_pretty<W: Write, T: Facet<'static>>(writer: &mut W, value: &T) -> Result<()> {
351    to_writer_with_options(writer, value, &SerializeOptions::default().pretty())
352}
353
354/// Serialize a value of type `T` to a writer as XML with custom options.
355///
356/// # Example
357///
358/// ```
359/// # use facet::Facet;
360/// # use facet_xml as xml;
361/// # use facet_xml::{to_writer_with_options, SerializeOptions};
362/// #[derive(Facet)]
363/// struct Person {
364///     #[facet(xml::attribute)]
365///     id: u32,
366///     #[facet(xml::element)]
367///     name: String,
368/// }
369///
370/// # fn main() -> Result<(), facet_xml::XmlError> {
371/// let person = Person { id: 42, name: "Alice".into() };
372///
373/// // Compact output (default)
374/// let mut buffer = Vec::new();
375/// to_writer_with_options(&mut buffer, &person, &SerializeOptions::default())?;
376/// assert_eq!(buffer, br#"<Person id="42"><name>Alice</name></Person>"#);
377///
378/// // Pretty output with default indent
379/// let mut buffer = Vec::new();
380/// to_writer_with_options(&mut buffer, &person, &SerializeOptions::default().pretty())?;
381///
382/// // Pretty output with custom indent (tabs)
383/// let mut buffer = Vec::new();
384/// to_writer_with_options(&mut buffer, &person, &SerializeOptions::default().indent("\t"))?;
385/// # Ok(())
386/// # }
387/// ```
388pub fn to_writer_with_options<W: Write, T: Facet<'static>>(
389    writer: &mut W,
390    value: &T,
391    options: &SerializeOptions,
392) -> Result<()> {
393    let peek = Peek::new(value);
394    let mut serializer = XmlSerializer::new(
395        writer,
396        options.indent_str(),
397        options.float_formatter,
398        options.preserve_entities,
399    );
400
401    // Get the type name for the root element, respecting `rename` attribute
402    let type_name = crate::deserialize::get_shape_display_name(peek.shape());
403    serializer.serialize_element(type_name, peek)
404}
405
406struct XmlSerializer<'a, W> {
407    writer: W,
408    /// Namespace URI -> prefix mapping for already-declared namespaces.
409    declared_namespaces: HashMap<String, String>,
410    /// Counter for auto-generating namespace prefixes (ns0, ns1, ...).
411    next_ns_index: usize,
412    /// Indentation string for pretty-printing (None for compact output).
413    indent: Option<&'a str>,
414    /// Current indentation depth.
415    depth: usize,
416    /// The currently active default namespace (from xmlns="..." on an ancestor).
417    /// Child elements in this namespace don't need to re-declare it.
418    current_default_ns: Option<String>,
419    /// Custom formatter for floating-point numbers.
420    float_formatter: Option<FloatFormatter>,
421    /// Whether to preserve entity references in string values.
422    preserve_entities: bool,
423}
424
425impl<'a, W: Write> XmlSerializer<'a, W> {
426    fn new(
427        writer: W,
428        indent: Option<&'a str>,
429        float_formatter: Option<FloatFormatter>,
430        preserve_entities: bool,
431    ) -> Self {
432        Self {
433            writer,
434            declared_namespaces: HashMap::new(),
435            next_ns_index: 0,
436            indent,
437            depth: 0,
438            current_default_ns: None,
439            float_formatter,
440            preserve_entities,
441        }
442    }
443
444    /// Write indentation for the current depth.
445    fn write_indent(&mut self) -> Result<()> {
446        if let Some(indent_str) = self.indent {
447            for _ in 0..self.depth {
448                self.writer
449                    .write_all(indent_str.as_bytes())
450                    .map_err(|e| XmlErrorKind::Io(e.to_string()))?;
451            }
452        }
453        Ok(())
454    }
455
456    /// Write a newline if pretty-printing is enabled.
457    fn write_newline(&mut self) -> Result<()> {
458        if self.indent.is_some() {
459            self.writer
460                .write_all(b"\n")
461                .map_err(|e| XmlErrorKind::Io(e.to_string()))?;
462        }
463        Ok(())
464    }
465
466    /// Get or create a prefix for the given namespace URI.
467    /// Returns the prefix (without colon).
468    ///
469    /// Note: We always need to emit xmlns declarations on each element that uses a prefix,
470    /// because XML namespace declarations are scoped to the element and its descendants.
471    /// A declaration on a sibling or earlier element doesn't apply.
472    fn get_or_create_prefix(&mut self, namespace_uri: &str) -> String {
473        // Check if we've already assigned a prefix to this URI
474        if let Some(prefix) = self.declared_namespaces.get(namespace_uri) {
475            return prefix.clone();
476        }
477
478        // Try well-known namespaces
479        let prefix = WELL_KNOWN_NAMESPACES
480            .iter()
481            .find(|(uri, _)| *uri == namespace_uri)
482            .map(|(_, prefix)| (*prefix).to_string())
483            .unwrap_or_else(|| {
484                // Auto-generate a prefix
485                let prefix = format!("ns{}", self.next_ns_index);
486                self.next_ns_index += 1;
487                prefix
488            });
489
490        // Ensure the prefix isn't already in use for a different namespace
491        let final_prefix = if self.declared_namespaces.values().any(|p| p == &prefix) {
492            // Conflict! Generate a new one
493            let prefix = format!("ns{}", self.next_ns_index);
494            self.next_ns_index += 1;
495            prefix
496        } else {
497            prefix
498        };
499
500        self.declared_namespaces
501            .insert(namespace_uri.to_string(), final_prefix.clone());
502        final_prefix
503    }
504
505    /// Get the effective namespace for a field, considering field-level xml::ns
506    /// and container-level xml::ns_all.
507    ///
508    /// For attributes: `ns_all` is NOT applied because unprefixed attributes in XML
509    /// are always in "no namespace", regardless of any default xmlns declaration.
510    /// Only explicit `xml::ns` on the attribute field is used.
511    ///
512    /// For elements: Both `xml::ns` and `ns_all` are considered.
513    fn get_field_namespace(
514        field: &Field,
515        ns_all: Option<&'static str>,
516        is_attribute: bool,
517    ) -> Option<&'static str> {
518        if is_attribute {
519            // Attributes only use explicit xml::ns, not ns_all
520            // Per XML spec: unprefixed attributes are in "no namespace"
521            field.xml_ns()
522        } else {
523            // Elements use xml::ns or fall back to ns_all
524            field.xml_ns().or(ns_all)
525        }
526    }
527
528    fn serialize_element<'mem, 'facet>(
529        &mut self,
530        element_name: &str,
531        peek: Peek<'mem, 'facet>,
532    ) -> Result<()> {
533        // Handle Option<T> - skip if None
534        if let Ok(opt_peek) = peek.into_option() {
535            if opt_peek.is_none() {
536                return Ok(());
537            }
538            if let Some(inner) = opt_peek.value() {
539                return self.serialize_element(element_name, inner);
540            }
541            return Ok(());
542        }
543
544        // Handle Spanned<T> - unwrap to the inner value
545        if is_spanned_shape(peek.shape())
546            && let Ok(struct_peek) = peek.into_struct()
547            && let Ok(value_field) = struct_peek.field_by_name("value")
548        {
549            return self.serialize_element(element_name, value_field);
550        }
551
552        // Check if this is a struct
553        let shape = peek.shape();
554        if let Ok(struct_peek) = peek.into_struct() {
555            return self.serialize_struct_as_element(element_name, struct_peek, shape);
556        }
557
558        // Check if this is an enum
559        if let Ok(enum_peek) = peek.into_enum() {
560            return self.serialize_enum_as_element(element_name, enum_peek);
561        }
562
563        // Check if this is a byte slice/array - serialize as base64
564        if self.try_serialize_bytes_as_element(element_name, peek)? {
565            return Ok(());
566        }
567
568        // Check if this is a list-like type (Vec, array, set)
569        if let Ok(list_peek) = peek.into_list_like() {
570            return self.serialize_list_as_element(element_name, list_peek);
571        }
572
573        // Check if this is a map
574        if let Ok(map_peek) = peek.into_map() {
575            return self.serialize_map_as_element(element_name, map_peek);
576        }
577
578        // For scalars/primitives, serialize as element with text content
579        write!(self.writer, "<{}>", escape_element_name(element_name))
580            .map_err(|e| XmlErrorKind::Io(e.to_string()))?;
581        self.serialize_value(peek)?;
582        write!(self.writer, "</{}>", escape_element_name(element_name))
583            .map_err(|e| XmlErrorKind::Io(e.to_string()))?;
584
585        Ok(())
586    }
587
588    fn serialize_enum_as_element<'mem, 'facet>(
589        &mut self,
590        element_name: &str,
591        enum_peek: facet_reflect::PeekEnum<'mem, 'facet>,
592    ) -> Result<()> {
593        let shape = enum_peek.shape();
594        let variant_name = enum_peek
595            .variant_name_active()
596            .map_err(|_| XmlErrorKind::SerializeUnknownElementType)?;
597
598        let fields: Vec<_> = enum_peek.fields_for_serialize().collect();
599
600        // Determine enum tagging strategy
601        let is_untagged = shape.is_untagged();
602        let tag_attr = shape.get_tag_attr();
603        let content_attr = shape.get_content_attr();
604
605        if is_untagged {
606            // Untagged: serialize content directly with element name
607            self.serialize_enum_content(element_name, variant_name, &fields)?;
608        } else if let Some(tag) = tag_attr {
609            if let Some(content) = content_attr {
610                // Adjacently tagged: <Element tag="Variant"><content>...</content></Element>
611                write!(
612                    self.writer,
613                    "<{} {}=\"{}\">",
614                    escape_element_name(element_name),
615                    escape_element_name(tag),
616                    escape_attribute_value(variant_name, self.preserve_entities)
617                )
618                .map_err(|e| XmlErrorKind::Io(e.to_string()))?;
619
620                if !fields.is_empty() {
621                    // Wrap content in the content element
622                    write!(self.writer, "<{}>", escape_element_name(content))
623                        .map_err(|e| XmlErrorKind::Io(e.to_string()))?;
624                    self.serialize_variant_fields(&fields)?;
625                    write!(self.writer, "</{}>", escape_element_name(content))
626                        .map_err(|e| XmlErrorKind::Io(e.to_string()))?;
627                }
628
629                write!(self.writer, "</{}>", escape_element_name(element_name))
630                    .map_err(|e| XmlErrorKind::Io(e.to_string()))?;
631            } else {
632                // Internally tagged: <Element tag="Variant">...fields...</Element>
633                write!(
634                    self.writer,
635                    "<{} {}=\"{}\">",
636                    escape_element_name(element_name),
637                    escape_element_name(tag),
638                    escape_attribute_value(variant_name, self.preserve_entities)
639                )
640                .map_err(|e| XmlErrorKind::Io(e.to_string()))?;
641
642                // Serialize fields directly (not wrapped in variant element)
643                self.serialize_variant_fields(&fields)?;
644
645                write!(self.writer, "</{}>", escape_element_name(element_name))
646                    .map_err(|e| XmlErrorKind::Io(e.to_string()))?;
647            }
648        } else {
649            // Externally tagged (default): <Element><Variant>...</Variant></Element>
650            write!(self.writer, "<{}>", escape_element_name(element_name))
651                .map_err(|e| XmlErrorKind::Io(e.to_string()))?;
652
653            if fields.is_empty() {
654                // Unit variant - just the variant name as an empty element
655                write!(self.writer, "<{}/>", escape_element_name(variant_name))
656                    .map_err(|e| XmlErrorKind::Io(e.to_string()))?;
657            } else if fields.len() == 1 && fields[0].0.name.parse::<usize>().is_ok() {
658                // Newtype variant - serialize the inner value with variant name
659                self.serialize_element(variant_name, fields[0].1)?;
660            } else {
661                // Struct-like variant
662                write!(self.writer, "<{}>", escape_element_name(variant_name))
663                    .map_err(|e| XmlErrorKind::Io(e.to_string()))?;
664
665                for (field_item, field_peek) in fields {
666                    self.serialize_element(field_item.name, field_peek)?;
667                }
668
669                write!(self.writer, "</{}>", escape_element_name(variant_name))
670                    .map_err(|e| XmlErrorKind::Io(e.to_string()))?;
671            }
672
673            write!(self.writer, "</{}>", escape_element_name(element_name))
674                .map_err(|e| XmlErrorKind::Io(e.to_string()))?;
675        }
676
677        Ok(())
678    }
679
680    /// Serialize enum content for untagged enums
681    fn serialize_enum_content<'mem, 'facet>(
682        &mut self,
683        element_name: &str,
684        variant_name: &str,
685        fields: &[(facet_reflect::FieldItem, Peek<'mem, 'facet>)],
686    ) -> Result<()> {
687        if fields.is_empty() {
688            // Unit variant - empty element
689            write!(self.writer, "<{}/>", escape_element_name(element_name))
690                .map_err(|e| XmlErrorKind::Io(e.to_string()))?;
691        } else if fields.len() == 1 && fields[0].0.name.parse::<usize>().is_ok() {
692            // Newtype variant - serialize inner directly
693            self.serialize_element(element_name, fields[0].1)?;
694        } else {
695            // Struct-like variant - serialize as struct
696            write!(self.writer, "<{}>", escape_element_name(element_name))
697                .map_err(|e| XmlErrorKind::Io(e.to_string()))?;
698
699            for (field_item, field_peek) in fields {
700                // Use variant_name as hint for structs in untagged context
701                let _ = variant_name; // Available if needed for disambiguation
702                self.serialize_element(field_item.name, *field_peek)?;
703            }
704
705            write!(self.writer, "</{}>", escape_element_name(element_name))
706                .map_err(|e| XmlErrorKind::Io(e.to_string()))?;
707        }
708        Ok(())
709    }
710
711    /// Serialize variant fields without wrapper
712    fn serialize_variant_fields<'mem, 'facet>(
713        &mut self,
714        fields: &[(facet_reflect::FieldItem, Peek<'mem, 'facet>)],
715    ) -> Result<()> {
716        if fields.len() == 1 && fields[0].0.name.parse::<usize>().is_ok() {
717            // Single tuple field - serialize value directly
718            self.serialize_value(fields[0].1)?;
719        } else {
720            for (field_item, field_peek) in fields {
721                self.serialize_element(field_item.name, *field_peek)?;
722            }
723        }
724        Ok(())
725    }
726
727    fn serialize_list_as_element<'mem, 'facet>(
728        &mut self,
729        element_name: &str,
730        list_peek: facet_reflect::PeekListLike<'mem, 'facet>,
731    ) -> Result<()> {
732        write!(self.writer, "<{}>", escape_element_name(element_name))
733            .map_err(|e| XmlErrorKind::Io(e.to_string()))?;
734
735        let has_items = list_peek.iter().next().is_some();
736        if has_items {
737            self.depth += 1;
738        }
739
740        for item in list_peek.iter() {
741            self.write_newline()?;
742            self.write_indent()?;
743            self.serialize_list_item_element(item)?;
744        }
745
746        if has_items {
747            self.depth -= 1;
748            self.write_newline()?;
749            self.write_indent()?;
750        }
751
752        write!(self.writer, "</{}>", escape_element_name(element_name))
753            .map_err(|e| XmlErrorKind::Io(e.to_string()))?;
754
755        Ok(())
756    }
757
758    fn serialize_map_as_element<'mem, 'facet>(
759        &mut self,
760        element_name: &str,
761        map_peek: facet_reflect::PeekMap<'mem, 'facet>,
762    ) -> Result<()> {
763        write!(self.writer, "<{}>", escape_element_name(element_name))
764            .map_err(|e| XmlErrorKind::Io(e.to_string()))?;
765
766        let has_items = map_peek.iter().next().is_some();
767        if has_items {
768            self.depth += 1;
769        }
770
771        for (key, value) in map_peek.iter() {
772            self.write_newline()?;
773            self.write_indent()?;
774            // Use the key as the element name
775            if let Some(key_str) = key.as_str() {
776                self.serialize_element(key_str, value)?;
777            } else if let Some(key_val) = value_to_string(key, self.float_formatter) {
778                self.serialize_element(&key_val, value)?;
779            } else {
780                // Fallback: use "entry" as element name with key as text
781                write!(self.writer, "<entry>").map_err(|e| XmlErrorKind::Io(e.to_string()))?;
782                self.serialize_value(key)?;
783                self.serialize_value(value)?;
784                write!(self.writer, "</entry>").map_err(|e| XmlErrorKind::Io(e.to_string()))?;
785            }
786        }
787
788        if has_items {
789            self.depth -= 1;
790            self.write_newline()?;
791            self.write_indent()?;
792        }
793
794        write!(self.writer, "</{}>", escape_element_name(element_name))
795            .map_err(|e| XmlErrorKind::Io(e.to_string()))?;
796
797        Ok(())
798    }
799
800    /// Try to serialize bytes (`Vec<u8>`, `&[u8]`, `[u8; N]`) as base64-encoded element.
801    /// Returns Ok(true) if bytes were handled, Ok(false) if not bytes.
802    fn try_serialize_bytes_as_element<'mem, 'facet>(
803        &mut self,
804        element_name: &str,
805        peek: Peek<'mem, 'facet>,
806    ) -> Result<bool> {
807        let shape = peek.shape();
808
809        // Check for Vec<u8>
810        if let Def::List(ld) = &shape.def
811            && ld.t().is_type::<u8>()
812            && let Some(bytes) = peek.as_bytes()
813        {
814            let encoded = BASE64_STANDARD.encode(bytes);
815            write!(self.writer, "<{}>", escape_element_name(element_name))
816                .map_err(|e| XmlErrorKind::Io(e.to_string()))?;
817            write!(
818                self.writer,
819                "{}",
820                escape_text(&encoded, self.preserve_entities)
821            )
822            .map_err(|e| XmlErrorKind::Io(e.to_string()))?;
823            write!(self.writer, "</{}>", escape_element_name(element_name))
824                .map_err(|e| XmlErrorKind::Io(e.to_string()))?;
825            return Ok(true);
826        }
827
828        // Check for [u8; N]
829        if let Def::Array(ad) = &shape.def
830            && ad.t().is_type::<u8>()
831        {
832            // Collect bytes from the array
833            if let Ok(list_peek) = peek.into_list_like() {
834                let bytes: Vec<u8> = list_peek
835                    .iter()
836                    .filter_map(|p| p.get::<u8>().ok().copied())
837                    .collect();
838                let encoded = BASE64_STANDARD.encode(&bytes);
839                write!(self.writer, "<{}>", escape_element_name(element_name))
840                    .map_err(|e| XmlErrorKind::Io(e.to_string()))?;
841                write!(
842                    self.writer,
843                    "{}",
844                    escape_text(&encoded, self.preserve_entities)
845                )
846                .map_err(|e| XmlErrorKind::Io(e.to_string()))?;
847                write!(self.writer, "</{}>", escape_element_name(element_name))
848                    .map_err(|e| XmlErrorKind::Io(e.to_string()))?;
849                return Ok(true);
850            }
851        }
852
853        // Check for &[u8]
854        if let Def::Slice(sd) = &shape.def
855            && sd.t().is_type::<u8>()
856            && let Some(bytes) = peek.as_bytes()
857        {
858            let encoded = BASE64_STANDARD.encode(bytes);
859            write!(self.writer, "<{}>", escape_element_name(element_name))
860                .map_err(|e| XmlErrorKind::Io(e.to_string()))?;
861            write!(
862                self.writer,
863                "{}",
864                escape_text(&encoded, self.preserve_entities)
865            )
866            .map_err(|e| XmlErrorKind::Io(e.to_string()))?;
867            write!(self.writer, "</{}>", escape_element_name(element_name))
868                .map_err(|e| XmlErrorKind::Io(e.to_string()))?;
869            return Ok(true);
870        }
871
872        Ok(false)
873    }
874
875    fn serialize_struct_as_element<'mem, 'facet>(
876        &mut self,
877        element_name: &str,
878        struct_peek: facet_reflect::PeekStruct<'mem, 'facet>,
879        shape: &'static Shape,
880    ) -> Result<()> {
881        let struct_ty = struct_peek.ty();
882
883        match struct_ty.kind {
884            StructKind::Unit => {
885                // Unit struct - just output empty element
886                write!(self.writer, "<{}/>", escape_element_name(element_name))
887                    .map_err(|e| XmlErrorKind::Io(e.to_string()))?;
888                return Ok(());
889            }
890            StructKind::Tuple | StructKind::TupleStruct => {
891                // Tuple struct - serialize fields in order as child elements
892                let fields: Vec<_> = struct_peek.fields_for_serialize().collect();
893                if fields.is_empty() {
894                    write!(self.writer, "<{}/>", escape_element_name(element_name))
895                        .map_err(|e| XmlErrorKind::Io(e.to_string()))?;
896                    return Ok(());
897                }
898
899                write!(self.writer, "<{}>", escape_element_name(element_name))
900                    .map_err(|e| XmlErrorKind::Io(e.to_string()))?;
901
902                for (i, (_, field_peek)) in fields.into_iter().enumerate() {
903                    // Use indexed element names for tuple fields
904                    let field_name = format!("_{i}");
905                    self.serialize_element(&field_name, field_peek)?;
906                }
907
908                write!(self.writer, "</{}>", escape_element_name(element_name))
909                    .map_err(|e| XmlErrorKind::Io(e.to_string()))?;
910                return Ok(());
911            }
912            StructKind::Struct => {
913                // Named struct - fall through to normal handling
914            }
915        }
916
917        let fields = struct_ty.fields;
918        let missing = fields_missing_xml_annotations(fields, XmlAnnotationPhase::Serialize);
919        if !missing.is_empty() {
920            let field_info = missing
921                .into_iter()
922                .map(|field| (field.name, field.shape().type_identifier))
923                .collect();
924            return Err(XmlError::new(XmlErrorKind::MissingXmlAnnotations {
925                type_name: shape.type_identifier,
926                phase: MissingAnnotationPhase::Serialize,
927                fields: field_info,
928            }));
929        }
930
931        // Get container-level namespace default
932        let ns_all = shape.xml_ns_all();
933
934        // Collect attributes (with field info for namespace), elements, and text content
935        struct AttrInfo<'a> {
936            name: &'a str,
937            value: String,
938            namespace: Option<&'static str>,
939        }
940        let mut attributes: Vec<AttrInfo> = Vec::new();
941        let mut elements: Vec<(facet_reflect::FieldItem, Peek<'mem, 'facet>)> = Vec::new();
942        let mut elements_list: Vec<Peek<'mem, 'facet>> = Vec::new();
943        let mut text_content: Option<Peek<'mem, 'facet>> = None;
944
945        for (field_item, field_peek) in struct_peek.fields_for_serialize() {
946            let field = &field_item.field;
947
948            // Handle custom serialization for attributes - get value immediately
949            if field.is_xml_attribute() {
950                let value = if field.proxy_convert_out_fn().is_some() {
951                    // Get the intermediate representation for serialization
952                    if let Ok(owned) = field_peek.custom_serialization(*field) {
953                        value_to_string(owned.as_peek(), self.float_formatter)
954                    } else {
955                        value_to_string(field_peek, self.float_formatter)
956                    }
957                } else {
958                    value_to_string(field_peek, self.float_formatter)
959                };
960                if let Some(value) = value {
961                    // Pass is_attribute=true so ns_all is NOT applied
962                    let namespace = Self::get_field_namespace(field, ns_all, true);
963                    attributes.push(AttrInfo {
964                        name: field_item.name,
965                        value,
966                        namespace,
967                    });
968                }
969            } else if field.is_xml_element() {
970                elements.push((field_item, field_peek));
971            } else if field.is_xml_elements() {
972                elements_list.push(field_peek);
973            } else if field.is_xml_text() {
974                text_content = Some(field_peek);
975            }
976        }
977
978        // Determine if we need content
979        let has_content =
980            !elements.is_empty() || !elements_list.is_empty() || text_content.is_some();
981
982        // Collect xmlns declarations needed for attributes on this element
983        // We always emit xmlns declarations on the element that uses them, because
984        // XML namespace scope is limited to an element and its descendants.
985        let mut xmlns_decls: Vec<(String, String)> = Vec::new(); // (prefix, uri)
986        let mut attr_prefixes: Vec<Option<String>> = Vec::new(); // prefix for each attribute
987
988        for attr in &attributes {
989            if let Some(ns_uri) = attr.namespace {
990                let prefix = self.get_or_create_prefix(ns_uri);
991                // Always emit xmlns declaration on this element
992                if !xmlns_decls.iter().any(|(_, u)| u == ns_uri) {
993                    xmlns_decls.push((prefix.clone(), ns_uri.to_string()));
994                }
995                attr_prefixes.push(Some(prefix));
996            } else {
997                attr_prefixes.push(None);
998            }
999        }
1000
1001        // Write opening tag
1002        write!(self.writer, "<{}", escape_element_name(element_name))
1003            .map_err(|e| XmlErrorKind::Io(e.to_string()))?;
1004
1005        // If ns_all is set and differs from the current default namespace,
1006        // emit a default namespace declaration (xmlns="...").
1007        // Child elements with the same namespace will be unprefixed and inherit it.
1008        let emitting_new_default_ns = if let Some(ns_uri) = ns_all {
1009            let dominated = self
1010                .current_default_ns
1011                .as_ref()
1012                .is_some_and(|current| current == ns_uri);
1013            if !dominated {
1014                write!(
1015                    self.writer,
1016                    " xmlns=\"{}\"",
1017                    escape_attribute_value(ns_uri, self.preserve_entities)
1018                )
1019                .map_err(|e| XmlErrorKind::Io(e.to_string()))?;
1020                true
1021            } else {
1022                false
1023            }
1024        } else {
1025            false
1026        };
1027
1028        // Write xmlns declarations for attributes (only for explicitly namespaced attributes)
1029        for (prefix, uri) in &xmlns_decls {
1030            write!(
1031                self.writer,
1032                " xmlns:{}=\"{}\"",
1033                escape_element_name(prefix),
1034                escape_attribute_value(uri, self.preserve_entities)
1035            )
1036            .map_err(|e| XmlErrorKind::Io(e.to_string()))?;
1037        }
1038
1039        // Write attributes (with prefix if namespaced)
1040        for (attr, prefix) in attributes.iter().zip(attr_prefixes.iter()) {
1041            let attr_name = if let Some(p) = prefix {
1042                format!("{p}:{}", attr.name)
1043            } else {
1044                attr.name.to_string()
1045            };
1046            write!(
1047                self.writer,
1048                " {}=\"{}\"",
1049                escape_element_name(&attr_name),
1050                escape_attribute_value(&attr.value, self.preserve_entities)
1051            )
1052            .map_err(|e| XmlErrorKind::Io(e.to_string()))?;
1053        }
1054
1055        if !has_content {
1056            // Self-closing tag
1057            write!(self.writer, "/>").map_err(|e| XmlErrorKind::Io(e.to_string()))?;
1058            return Ok(());
1059        }
1060
1061        write!(self.writer, ">").map_err(|e| XmlErrorKind::Io(e.to_string()))?;
1062
1063        // Save and update the default namespace for children
1064        let old_default_ns = if emitting_new_default_ns {
1065            let old = self.current_default_ns.take();
1066            self.current_default_ns = ns_all.map(|s| s.to_string());
1067            old
1068        } else {
1069            None
1070        };
1071
1072        // Write text content if present (no indentation for text content)
1073        if let Some(text_peek) = text_content {
1074            self.serialize_text_value(text_peek)?;
1075        }
1076
1077        // Check if we have child elements (for indentation purposes)
1078        let has_child_elements = !elements.is_empty() || !elements_list.is_empty();
1079
1080        // Write child elements (with namespace support)
1081        // Pass ns_all as the default namespace so child elements with matching
1082        // namespace use unprefixed form (they inherit the default xmlns).
1083        if has_child_elements {
1084            self.depth += 1;
1085        }
1086
1087        for (field_item, field_peek) in elements {
1088            // Pass is_attribute=false for elements
1089            let field_ns = Self::get_field_namespace(&field_item.field, ns_all, false);
1090
1091            self.write_newline()?;
1092            self.write_indent()?;
1093
1094            // Handle custom serialization for elements
1095            if field_item.field.proxy_convert_out_fn().is_some() {
1096                if let Ok(owned) = field_peek.custom_serialization(field_item.field) {
1097                    self.serialize_namespaced_element(
1098                        field_item.name,
1099                        owned.as_peek(),
1100                        field_ns,
1101                        ns_all,
1102                    )?;
1103                } else {
1104                    self.serialize_namespaced_element(
1105                        field_item.name,
1106                        field_peek,
1107                        field_ns,
1108                        ns_all,
1109                    )?;
1110                }
1111            } else {
1112                self.serialize_namespaced_element(field_item.name, field_peek, field_ns, ns_all)?;
1113            }
1114        }
1115
1116        // Write elements lists
1117        for field_peek in elements_list {
1118            self.serialize_elements_list(field_peek)?;
1119        }
1120
1121        if has_child_elements {
1122            self.depth -= 1;
1123            self.write_newline()?;
1124            self.write_indent()?;
1125        }
1126
1127        // Restore the old default namespace
1128        if emitting_new_default_ns {
1129            self.current_default_ns = old_default_ns;
1130        }
1131
1132        // Write closing tag
1133        write!(self.writer, "</{}>", escape_element_name(element_name))
1134            .map_err(|e| XmlErrorKind::Io(e.to_string()))?;
1135
1136        Ok(())
1137    }
1138
1139    /// Serialize an element with optional namespace.
1140    ///
1141    /// - `namespace`: The namespace this element should be in
1142    /// - `default_ns`: The currently active default namespace (from parent's xmlns="...")
1143    ///
1144    /// If the element's namespace matches the default namespace, we use an unprefixed
1145    /// element name (the element inherits the default namespace).
1146    /// Otherwise, we use a prefix and emit an xmlns:prefix declaration.
1147    fn serialize_namespaced_element<'mem, 'facet>(
1148        &mut self,
1149        element_name: &str,
1150        peek: Peek<'mem, 'facet>,
1151        namespace: Option<&str>,
1152        default_ns: Option<&str>,
1153    ) -> Result<()> {
1154        // Handle Option<T> - skip if None
1155        if let Ok(opt_peek) = peek.into_option() {
1156            if opt_peek.is_none() {
1157                return Ok(());
1158            }
1159            if let Some(inner) = opt_peek.value() {
1160                return self.serialize_namespaced_element(
1161                    element_name,
1162                    inner,
1163                    namespace,
1164                    default_ns,
1165                );
1166            }
1167            return Ok(());
1168        }
1169
1170        // Handle Spanned<T> - unwrap to the inner value
1171        if is_spanned_shape(peek.shape())
1172            && let Ok(struct_peek) = peek.into_struct()
1173            && let Ok(value_field) = struct_peek.field_by_name("value")
1174        {
1175            return self.serialize_namespaced_element(
1176                element_name,
1177                value_field,
1178                namespace,
1179                default_ns,
1180            );
1181        }
1182
1183        // Determine element name and xmlns declaration
1184        // If namespace matches the current default, use unprefixed form (inherit default).
1185        // Otherwise, use a prefix and emit xmlns:prefix declaration.
1186        let (final_name, xmlns_decl) = if let Some(ns_uri) = namespace {
1187            if default_ns == Some(ns_uri) {
1188                // Element is in the default namespace - use unprefixed form
1189                (element_name.to_string(), None)
1190            } else {
1191                // Element is in a different namespace - use prefix
1192                let prefix = self.get_or_create_prefix(ns_uri);
1193                let prefixed = format!("{prefix}:{element_name}");
1194                (prefixed, Some((prefix, ns_uri.to_string())))
1195            }
1196        } else {
1197            (element_name.to_string(), None)
1198        };
1199
1200        // Check if this is a struct - handle specially for proper namespace propagation
1201        let shape = peek.shape();
1202        if let Ok(struct_peek) = peek.into_struct() {
1203            return self.serialize_struct_as_namespaced_element(
1204                &final_name,
1205                struct_peek,
1206                xmlns_decl,
1207                shape,
1208                default_ns,
1209            );
1210        }
1211
1212        // Check if this is an enum
1213        if let Ok(enum_peek) = peek.into_enum() {
1214            return self.serialize_enum_as_namespaced_element(&final_name, enum_peek, xmlns_decl);
1215        }
1216
1217        // For scalars/primitives, serialize as element with text content
1218        write!(self.writer, "<{}", escape_element_name(&final_name))
1219            .map_err(|e| XmlErrorKind::Io(e.to_string()))?;
1220
1221        // Add xmlns declaration if needed
1222        if let Some((prefix, uri)) = xmlns_decl {
1223            write!(
1224                self.writer,
1225                " xmlns:{}=\"{}\"",
1226                escape_element_name(&prefix),
1227                escape_attribute_value(&uri, self.preserve_entities)
1228            )
1229            .map_err(|e| XmlErrorKind::Io(e.to_string()))?;
1230        }
1231
1232        write!(self.writer, ">").map_err(|e| XmlErrorKind::Io(e.to_string()))?;
1233        self.serialize_value(peek)?;
1234        write!(self.writer, "</{}>", escape_element_name(&final_name))
1235            .map_err(|e| XmlErrorKind::Io(e.to_string()))?;
1236
1237        Ok(())
1238    }
1239
1240    /// Serialize a struct as an element with optional xmlns declaration on the opening tag.
1241    ///
1242    /// - `parent_default_ns`: The default namespace inherited from the parent element
1243    fn serialize_struct_as_namespaced_element<'mem, 'facet>(
1244        &mut self,
1245        element_name: &str,
1246        struct_peek: facet_reflect::PeekStruct<'mem, 'facet>,
1247        xmlns_decl: Option<(String, String)>,
1248        shape: &'static Shape,
1249        parent_default_ns: Option<&str>,
1250    ) -> Result<()> {
1251        match struct_peek.ty().kind {
1252            StructKind::Unit => {
1253                // Unit struct - just output empty element with xmlns if needed
1254                write!(self.writer, "<{}", escape_element_name(element_name))
1255                    .map_err(|e| XmlErrorKind::Io(e.to_string()))?;
1256                if let Some((prefix, uri)) = xmlns_decl {
1257                    write!(
1258                        self.writer,
1259                        " xmlns:{}=\"{}\"",
1260                        escape_element_name(&prefix),
1261                        escape_attribute_value(&uri, self.preserve_entities)
1262                    )
1263                    .map_err(|e| XmlErrorKind::Io(e.to_string()))?;
1264                }
1265                write!(self.writer, "/>").map_err(|e| XmlErrorKind::Io(e.to_string()))?;
1266                return Ok(());
1267            }
1268            StructKind::Tuple | StructKind::TupleStruct => {
1269                // Tuple struct - serialize fields in order as child elements
1270                let fields: Vec<_> = struct_peek.fields_for_serialize().collect();
1271                if fields.is_empty() {
1272                    write!(self.writer, "<{}", escape_element_name(element_name))
1273                        .map_err(|e| XmlErrorKind::Io(e.to_string()))?;
1274                    if let Some((prefix, uri)) = xmlns_decl {
1275                        write!(
1276                            self.writer,
1277                            " xmlns:{}=\"{}\"",
1278                            escape_element_name(&prefix),
1279                            escape_attribute_value(&uri, self.preserve_entities)
1280                        )
1281                        .map_err(|e| XmlErrorKind::Io(e.to_string()))?;
1282                    }
1283                    write!(self.writer, "/>").map_err(|e| XmlErrorKind::Io(e.to_string()))?;
1284                    return Ok(());
1285                }
1286
1287                write!(self.writer, "<{}", escape_element_name(element_name))
1288                    .map_err(|e| XmlErrorKind::Io(e.to_string()))?;
1289                if let Some((prefix, uri)) = xmlns_decl {
1290                    write!(
1291                        self.writer,
1292                        " xmlns:{}=\"{}\"",
1293                        escape_element_name(&prefix),
1294                        escape_attribute_value(&uri, self.preserve_entities)
1295                    )
1296                    .map_err(|e| XmlErrorKind::Io(e.to_string()))?;
1297                }
1298                write!(self.writer, ">").map_err(|e| XmlErrorKind::Io(e.to_string()))?;
1299
1300                for (i, (_, field_peek)) in fields.into_iter().enumerate() {
1301                    let field_name = format!("_{i}");
1302                    self.serialize_element(&field_name, field_peek)?;
1303                }
1304
1305                write!(self.writer, "</{}>", escape_element_name(element_name))
1306                    .map_err(|e| XmlErrorKind::Io(e.to_string()))?;
1307                return Ok(());
1308            }
1309            StructKind::Struct => {
1310                // Named struct - fall through to normal handling
1311            }
1312        }
1313
1314        // Get container-level namespace default for the nested struct
1315        let ns_all = shape.xml_ns_all();
1316
1317        // The actual default namespace in the XML is inherited from parent.
1318        // We don't emit xmlns="..." on nested elements because that would
1319        // change the element's own namespace. Child elements in a different
1320        // namespace will use prefixed form.
1321        let effective_default_ns: Option<&str> = parent_default_ns;
1322
1323        // Collect attributes, elements, and text content
1324        struct AttrInfo<'a> {
1325            name: &'a str,
1326            value: String,
1327            namespace: Option<&'static str>,
1328        }
1329        let mut attributes: Vec<AttrInfo> = Vec::new();
1330        let mut elements: Vec<(facet_reflect::FieldItem, Peek<'mem, 'facet>)> = Vec::new();
1331        let mut elements_list: Vec<Peek<'mem, 'facet>> = Vec::new();
1332        let mut text_content: Option<Peek<'mem, 'facet>> = None;
1333
1334        for (field_item, field_peek) in struct_peek.fields_for_serialize() {
1335            let field = &field_item.field;
1336
1337            if field.is_xml_attribute() {
1338                let value = if field.proxy_convert_out_fn().is_some() {
1339                    if let Ok(owned) = field_peek.custom_serialization(*field) {
1340                        value_to_string(owned.as_peek(), self.float_formatter)
1341                    } else {
1342                        value_to_string(field_peek, self.float_formatter)
1343                    }
1344                } else {
1345                    value_to_string(field_peek, self.float_formatter)
1346                };
1347                if let Some(value) = value {
1348                    // Pass is_attribute=true so ns_all is NOT applied
1349                    let namespace = Self::get_field_namespace(field, ns_all, true);
1350                    attributes.push(AttrInfo {
1351                        name: field_item.name,
1352                        value,
1353                        namespace,
1354                    });
1355                }
1356            } else if field.is_xml_element() {
1357                elements.push((field_item, field_peek));
1358            } else if field.is_xml_elements() {
1359                elements_list.push(field_peek);
1360            } else if field.is_xml_text() {
1361                text_content = Some(field_peek);
1362            }
1363        }
1364
1365        let has_content =
1366            !elements.is_empty() || !elements_list.is_empty() || text_content.is_some();
1367
1368        // Collect xmlns declarations needed for attributes
1369        // We always emit xmlns declarations on each element that uses them.
1370        let mut xmlns_decls: Vec<(String, String)> = Vec::new();
1371        let mut attr_prefixes: Vec<Option<String>> = Vec::new();
1372
1373        // Start with the element's own xmlns declaration if any (from prefixed form)
1374        if let Some((prefix, uri)) = xmlns_decl {
1375            xmlns_decls.push((prefix, uri));
1376        }
1377
1378        for attr in &attributes {
1379            if let Some(ns_uri) = attr.namespace {
1380                let prefix = self.get_or_create_prefix(ns_uri);
1381                // Always emit xmlns declaration on this element (if not already)
1382                if !xmlns_decls.iter().any(|(_, u)| u == ns_uri) {
1383                    xmlns_decls.push((prefix.clone(), ns_uri.to_string()));
1384                }
1385                attr_prefixes.push(Some(prefix));
1386            } else {
1387                attr_prefixes.push(None);
1388            }
1389        }
1390
1391        // Write opening tag
1392        write!(self.writer, "<{}", escape_element_name(element_name))
1393            .map_err(|e| XmlErrorKind::Io(e.to_string()))?;
1394
1395        // NOTE: We intentionally do NOT emit xmlns="..." on nested struct elements here.
1396        // The element itself is in the parent's namespace (determined by the context
1397        // where it's used). Only the struct's CHILD elements are affected by ns_all.
1398        // Child elements in a different namespace will use prefixed form.
1399
1400        // Write prefixed xmlns declarations (for explicitly namespaced attributes)
1401        for (prefix, uri) in &xmlns_decls {
1402            write!(
1403                self.writer,
1404                " xmlns:{}=\"{}\"",
1405                escape_element_name(prefix),
1406                escape_attribute_value(uri, self.preserve_entities)
1407            )
1408            .map_err(|e| XmlErrorKind::Io(e.to_string()))?;
1409        }
1410
1411        // Write attributes
1412        for (attr, prefix) in attributes.iter().zip(attr_prefixes.iter()) {
1413            let attr_name = if let Some(p) = prefix {
1414                format!("{p}:{}", attr.name)
1415            } else {
1416                attr.name.to_string()
1417            };
1418            write!(
1419                self.writer,
1420                " {}=\"{}\"",
1421                escape_element_name(&attr_name),
1422                escape_attribute_value(&attr.value, self.preserve_entities)
1423            )
1424            .map_err(|e| XmlErrorKind::Io(e.to_string()))?;
1425        }
1426
1427        if !has_content {
1428            write!(self.writer, "/>").map_err(|e| XmlErrorKind::Io(e.to_string()))?;
1429            return Ok(());
1430        }
1431
1432        write!(self.writer, ">").map_err(|e| XmlErrorKind::Io(e.to_string()))?;
1433
1434        if let Some(text_peek) = text_content {
1435            self.serialize_text_value(text_peek)?;
1436        }
1437
1438        // Check if we have child elements (for indentation purposes)
1439        let has_child_elements = !elements.is_empty() || !elements_list.is_empty();
1440
1441        if has_child_elements {
1442            self.depth += 1;
1443        }
1444
1445        for (field_item, field_peek) in elements {
1446            // Pass is_attribute=false for elements
1447            let field_ns = Self::get_field_namespace(&field_item.field, ns_all, false);
1448
1449            self.write_newline()?;
1450            self.write_indent()?;
1451
1452            if field_item.field.proxy_convert_out_fn().is_some() {
1453                if let Ok(owned) = field_peek.custom_serialization(field_item.field) {
1454                    self.serialize_namespaced_element(
1455                        field_item.name,
1456                        owned.as_peek(),
1457                        field_ns,
1458                        effective_default_ns,
1459                    )?;
1460                } else {
1461                    self.serialize_namespaced_element(
1462                        field_item.name,
1463                        field_peek,
1464                        field_ns,
1465                        effective_default_ns,
1466                    )?;
1467                }
1468            } else {
1469                self.serialize_namespaced_element(
1470                    field_item.name,
1471                    field_peek,
1472                    field_ns,
1473                    effective_default_ns,
1474                )?;
1475            }
1476        }
1477
1478        for field_peek in elements_list {
1479            self.serialize_elements_list(field_peek)?;
1480        }
1481
1482        if has_child_elements {
1483            self.depth -= 1;
1484            self.write_newline()?;
1485            self.write_indent()?;
1486        }
1487
1488        write!(self.writer, "</{}>", escape_element_name(element_name))
1489            .map_err(|e| XmlErrorKind::Io(e.to_string()))?;
1490
1491        Ok(())
1492    }
1493
1494    /// Serialize an enum as an element with optional xmlns declaration.
1495    fn serialize_enum_as_namespaced_element<'mem, 'facet>(
1496        &mut self,
1497        prefixed_element_name: &str,
1498        enum_peek: facet_reflect::PeekEnum<'mem, 'facet>,
1499        xmlns_decl: Option<(String, String)>,
1500    ) -> Result<()> {
1501        let shape = enum_peek.shape();
1502        let variant_name = enum_peek
1503            .variant_name_active()
1504            .map_err(|_| XmlErrorKind::SerializeUnknownElementType)?;
1505
1506        let fields: Vec<_> = enum_peek.fields_for_serialize().collect();
1507
1508        let is_untagged = shape.is_untagged();
1509        let tag_attr = shape.get_tag_attr();
1510        let content_attr = shape.get_content_attr();
1511
1512        // Helper to write opening tag with optional xmlns
1513        let write_open_tag =
1514            |writer: &mut W, name: &str, xmlns: &Option<(String, String)>| -> Result<()> {
1515                write!(writer, "<{}", escape_element_name(name))
1516                    .map_err(|e| XmlErrorKind::Io(e.to_string()))?;
1517                if let Some((prefix, uri)) = xmlns {
1518                    write!(
1519                        writer,
1520                        " xmlns:{}=\"{}\"",
1521                        escape_element_name(prefix),
1522                        escape_attribute_value(uri, self.preserve_entities)
1523                    )
1524                    .map_err(|e| XmlErrorKind::Io(e.to_string()))?;
1525                }
1526                Ok(())
1527            };
1528
1529        if is_untagged {
1530            // Untagged: serialize content directly
1531            if fields.is_empty() {
1532                write_open_tag(&mut self.writer, prefixed_element_name, &xmlns_decl)?;
1533                write!(self.writer, "/>").map_err(|e| XmlErrorKind::Io(e.to_string()))?;
1534            } else if fields.len() == 1 && fields[0].0.name.parse::<usize>().is_ok() {
1535                // Newtype variant
1536                write_open_tag(&mut self.writer, prefixed_element_name, &xmlns_decl)?;
1537                write!(self.writer, ">").map_err(|e| XmlErrorKind::Io(e.to_string()))?;
1538                self.serialize_value(fields[0].1)?;
1539                write!(
1540                    self.writer,
1541                    "</{}>",
1542                    escape_element_name(prefixed_element_name)
1543                )
1544                .map_err(|e| XmlErrorKind::Io(e.to_string()))?;
1545            } else {
1546                write_open_tag(&mut self.writer, prefixed_element_name, &xmlns_decl)?;
1547                write!(self.writer, ">").map_err(|e| XmlErrorKind::Io(e.to_string()))?;
1548                for (field_item, field_peek) in fields {
1549                    self.serialize_element(field_item.name, field_peek)?;
1550                }
1551                write!(
1552                    self.writer,
1553                    "</{}>",
1554                    escape_element_name(prefixed_element_name)
1555                )
1556                .map_err(|e| XmlErrorKind::Io(e.to_string()))?;
1557            }
1558        } else if let Some(tag) = tag_attr {
1559            if let Some(content) = content_attr {
1560                // Adjacently tagged
1561                write_open_tag(&mut self.writer, prefixed_element_name, &xmlns_decl)?;
1562                write!(
1563                    self.writer,
1564                    " {}=\"{}\">",
1565                    escape_element_name(tag),
1566                    escape_attribute_value(variant_name, self.preserve_entities)
1567                )
1568                .map_err(|e| XmlErrorKind::Io(e.to_string()))?;
1569
1570                if !fields.is_empty() {
1571                    write!(self.writer, "<{}>", escape_element_name(content))
1572                        .map_err(|e| XmlErrorKind::Io(e.to_string()))?;
1573                    self.serialize_variant_fields(&fields)?;
1574                    write!(self.writer, "</{}>", escape_element_name(content))
1575                        .map_err(|e| XmlErrorKind::Io(e.to_string()))?;
1576                }
1577
1578                write!(
1579                    self.writer,
1580                    "</{}>",
1581                    escape_element_name(prefixed_element_name)
1582                )
1583                .map_err(|e| XmlErrorKind::Io(e.to_string()))?;
1584            } else {
1585                // Internally tagged
1586                write_open_tag(&mut self.writer, prefixed_element_name, &xmlns_decl)?;
1587                write!(
1588                    self.writer,
1589                    " {}=\"{}\">",
1590                    escape_element_name(tag),
1591                    escape_attribute_value(variant_name, self.preserve_entities)
1592                )
1593                .map_err(|e| XmlErrorKind::Io(e.to_string()))?;
1594                self.serialize_variant_fields(&fields)?;
1595                write!(
1596                    self.writer,
1597                    "</{}>",
1598                    escape_element_name(prefixed_element_name)
1599                )
1600                .map_err(|e| XmlErrorKind::Io(e.to_string()))?;
1601            }
1602        } else {
1603            // Externally tagged (default)
1604            write_open_tag(&mut self.writer, prefixed_element_name, &xmlns_decl)?;
1605            write!(self.writer, ">").map_err(|e| XmlErrorKind::Io(e.to_string()))?;
1606
1607            if fields.is_empty() {
1608                write!(self.writer, "<{}/>", escape_element_name(variant_name))
1609                    .map_err(|e| XmlErrorKind::Io(e.to_string()))?;
1610            } else if fields.len() == 1 && fields[0].0.name.parse::<usize>().is_ok() {
1611                self.serialize_element(variant_name, fields[0].1)?;
1612            } else {
1613                write!(self.writer, "<{}>", escape_element_name(variant_name))
1614                    .map_err(|e| XmlErrorKind::Io(e.to_string()))?;
1615                for (field_item, field_peek) in fields {
1616                    self.serialize_element(field_item.name, field_peek)?;
1617                }
1618                write!(self.writer, "</{}>", escape_element_name(variant_name))
1619                    .map_err(|e| XmlErrorKind::Io(e.to_string()))?;
1620            }
1621
1622            write!(
1623                self.writer,
1624                "</{}>",
1625                escape_element_name(prefixed_element_name)
1626            )
1627            .map_err(|e| XmlErrorKind::Io(e.to_string()))?;
1628        }
1629
1630        Ok(())
1631    }
1632
1633    fn serialize_elements_list<'mem, 'facet>(&mut self, peek: Peek<'mem, 'facet>) -> Result<()> {
1634        let list_peek = peek
1635            .into_list()
1636            .map_err(|_| XmlErrorKind::SerializeNotList)?;
1637
1638        for item_peek in list_peek.iter() {
1639            self.write_newline()?;
1640            self.write_indent()?;
1641            self.serialize_list_item_element(item_peek)?;
1642        }
1643
1644        Ok(())
1645    }
1646
1647    fn serialize_list_item_element<'mem, 'facet>(
1648        &mut self,
1649        peek: Peek<'mem, 'facet>,
1650    ) -> Result<()> {
1651        // For enums, use variant name as element name
1652        if let Ok(enum_peek) = peek.into_enum() {
1653            let variant_name = enum_peek
1654                .variant_name_active()
1655                .map_err(|_| XmlErrorKind::SerializeUnknownElementType)?;
1656
1657            // Get the variant's fields (respecting skip_serializing)
1658            let fields: Vec<_> = enum_peek.fields_for_serialize().collect();
1659
1660            if fields.is_empty() {
1661                // Unit variant - empty element
1662                write!(self.writer, "<{}/>", escape_element_name(variant_name))
1663                    .map_err(|e| XmlErrorKind::Io(e.to_string()))?;
1664            } else if fields.len() == 1 && fields[0].0.name.parse::<usize>().is_ok() {
1665                // Tuple variant with single field - serialize the inner value
1666                self.serialize_element(variant_name, fields[0].1)?;
1667            } else {
1668                // Struct-like variant
1669                write!(self.writer, "<{}>", escape_element_name(variant_name))
1670                    .map_err(|e| XmlErrorKind::Io(e.to_string()))?;
1671
1672                for (field_item, field_peek) in fields {
1673                    self.serialize_element(field_item.name, field_peek)?;
1674                }
1675
1676                write!(self.writer, "</{}>", escape_element_name(variant_name))
1677                    .map_err(|e| XmlErrorKind::Io(e.to_string()))?;
1678            }
1679            return Ok(());
1680        }
1681
1682        // For structs, use type name as element name
1683        let type_name = peek.shape().type_identifier;
1684        self.serialize_element(type_name, peek)
1685    }
1686
1687    fn serialize_text_value<'mem, 'facet>(&mut self, peek: Peek<'mem, 'facet>) -> Result<()> {
1688        // Handle Option<T>
1689        if let Ok(opt_peek) = peek.into_option() {
1690            if opt_peek.is_none() {
1691                return Ok(());
1692            }
1693            if let Some(inner) = opt_peek.value() {
1694                return self.serialize_text_value(inner);
1695            }
1696            return Ok(());
1697        }
1698
1699        // Handle Spanned<T>
1700        if is_spanned_shape(peek.shape())
1701            && let Ok(struct_peek) = peek.into_struct()
1702            && let Ok(value_peek) = struct_peek.field_by_name("value")
1703        {
1704            return self.serialize_text_value(value_peek);
1705        }
1706
1707        self.serialize_value(peek)
1708    }
1709
1710    fn serialize_value<'mem, 'facet>(&mut self, peek: Peek<'mem, 'facet>) -> Result<()> {
1711        // Handle Option<T>
1712        if let Ok(opt_peek) = peek.into_option() {
1713            if opt_peek.is_none() {
1714                return Ok(());
1715            }
1716            if let Some(inner) = opt_peek.value() {
1717                return self.serialize_value(inner);
1718            }
1719            return Ok(());
1720        }
1721
1722        // Handle Spanned<T>
1723        if is_spanned_shape(peek.shape())
1724            && let Ok(struct_peek) = peek.into_struct()
1725            && let Ok(value_peek) = struct_peek.field_by_name("value")
1726        {
1727            return self.serialize_value(value_peek);
1728        }
1729
1730        // Unwrap transparent wrappers
1731        let peek = peek.innermost_peek();
1732
1733        // Try string first
1734        if let Some(s) = peek.as_str() {
1735            write!(self.writer, "{}", escape_text(s, self.preserve_entities))
1736                .map_err(|e| XmlErrorKind::Io(e.to_string()))?;
1737            return Ok(());
1738        }
1739
1740        // Try various types
1741        if let Ok(v) = peek.get::<bool>() {
1742            write!(self.writer, "{v}").map_err(|e| XmlErrorKind::Io(e.to_string()))?;
1743            return Ok(());
1744        }
1745
1746        if let Ok(v) = peek.get::<i8>() {
1747            write!(self.writer, "{v}").map_err(|e| XmlErrorKind::Io(e.to_string()))?;
1748            return Ok(());
1749        }
1750        if let Ok(v) = peek.get::<i16>() {
1751            write!(self.writer, "{v}").map_err(|e| XmlErrorKind::Io(e.to_string()))?;
1752            return Ok(());
1753        }
1754        if let Ok(v) = peek.get::<i32>() {
1755            write!(self.writer, "{v}").map_err(|e| XmlErrorKind::Io(e.to_string()))?;
1756            return Ok(());
1757        }
1758        if let Ok(v) = peek.get::<i64>() {
1759            write!(self.writer, "{v}").map_err(|e| XmlErrorKind::Io(e.to_string()))?;
1760            return Ok(());
1761        }
1762
1763        if let Ok(v) = peek.get::<u8>() {
1764            write!(self.writer, "{v}").map_err(|e| XmlErrorKind::Io(e.to_string()))?;
1765            return Ok(());
1766        }
1767        if let Ok(v) = peek.get::<u16>() {
1768            write!(self.writer, "{v}").map_err(|e| XmlErrorKind::Io(e.to_string()))?;
1769            return Ok(());
1770        }
1771        if let Ok(v) = peek.get::<u32>() {
1772            write!(self.writer, "{v}").map_err(|e| XmlErrorKind::Io(e.to_string()))?;
1773            return Ok(());
1774        }
1775        if let Ok(v) = peek.get::<u64>() {
1776            write!(self.writer, "{v}").map_err(|e| XmlErrorKind::Io(e.to_string()))?;
1777            return Ok(());
1778        }
1779
1780        if let Ok(v) = peek.get::<f32>() {
1781            if let Some(fmt) = self.float_formatter {
1782                fmt(f64::from(*v), &mut self.writer)
1783                    .map_err(|e| XmlErrorKind::Io(e.to_string()))?;
1784            } else {
1785                write!(self.writer, "{v}").map_err(|e| XmlErrorKind::Io(e.to_string()))?;
1786            }
1787            return Ok(());
1788        }
1789        if let Ok(v) = peek.get::<f64>() {
1790            if let Some(fmt) = self.float_formatter {
1791                fmt(*v, &mut self.writer).map_err(|e| XmlErrorKind::Io(e.to_string()))?;
1792            } else {
1793                write!(self.writer, "{v}").map_err(|e| XmlErrorKind::Io(e.to_string()))?;
1794            }
1795            return Ok(());
1796        }
1797
1798        if let Ok(v) = peek.get::<char>() {
1799            write!(
1800                self.writer,
1801                "{}",
1802                escape_text(&v.to_string(), self.preserve_entities)
1803            )
1804            .map_err(|e| XmlErrorKind::Io(e.to_string()))?;
1805            return Ok(());
1806        }
1807
1808        Err(XmlErrorKind::SerializeUnknownValueType.into())
1809    }
1810}
1811
1812/// Convert a Peek value to a string representation, handling Options, Spanned, and transparent wrappers.
1813fn value_to_string<'mem, 'facet>(
1814    peek: Peek<'mem, 'facet>,
1815    float_formatter: Option<FloatFormatter>,
1816) -> Option<String> {
1817    // Handle Option<T>
1818    if let Ok(opt_peek) = peek.into_option() {
1819        if opt_peek.is_none() {
1820            return None;
1821        }
1822        if let Some(inner) = opt_peek.value() {
1823            return value_to_string(inner, float_formatter);
1824        }
1825        return None;
1826    }
1827
1828    // Handle Spanned<T>
1829    if is_spanned_shape(peek.shape())
1830        && let Ok(struct_peek) = peek.into_struct()
1831        && let Ok(value_peek) = struct_peek.field_by_name("value")
1832    {
1833        return value_to_string(value_peek, float_formatter);
1834    }
1835
1836    // Unwrap transparent wrappers
1837    let peek = peek.innermost_peek();
1838
1839    // Try string first
1840    if let Some(s) = peek.as_str() {
1841        return Some(s.to_string());
1842    }
1843
1844    // Try various types
1845    if let Ok(v) = peek.get::<bool>() {
1846        return Some(v.to_string());
1847    }
1848
1849    if let Ok(v) = peek.get::<i8>() {
1850        return Some(v.to_string());
1851    }
1852    if let Ok(v) = peek.get::<i16>() {
1853        return Some(v.to_string());
1854    }
1855    if let Ok(v) = peek.get::<i32>() {
1856        return Some(v.to_string());
1857    }
1858    if let Ok(v) = peek.get::<i64>() {
1859        return Some(v.to_string());
1860    }
1861
1862    if let Ok(v) = peek.get::<u8>() {
1863        return Some(v.to_string());
1864    }
1865    if let Ok(v) = peek.get::<u16>() {
1866        return Some(v.to_string());
1867    }
1868    if let Ok(v) = peek.get::<u32>() {
1869        return Some(v.to_string());
1870    }
1871    if let Ok(v) = peek.get::<u64>() {
1872        return Some(v.to_string());
1873    }
1874
1875    if let Ok(v) = peek.get::<f32>() {
1876        if let Some(fmt) = float_formatter {
1877            let mut buf = Vec::new();
1878            if fmt(f64::from(*v), &mut buf).is_ok() {
1879                return String::from_utf8(buf).ok();
1880            }
1881        }
1882        return Some(v.to_string());
1883    }
1884    if let Ok(v) = peek.get::<f64>() {
1885        if let Some(fmt) = float_formatter {
1886            let mut buf = Vec::new();
1887            if fmt(*v, &mut buf).is_ok() {
1888                return String::from_utf8(buf).ok();
1889            }
1890        }
1891        return Some(v.to_string());
1892    }
1893
1894    if let Ok(v) = peek.get::<char>() {
1895        return Some(v.to_string());
1896    }
1897
1898    None
1899}
1900
1901/// Escape special characters in XML text content.
1902fn escape_text(s: &str, preserve_entities: bool) -> String {
1903    if preserve_entities {
1904        escape_preserving_entities(s, false)
1905    } else {
1906        let mut result = String::with_capacity(s.len());
1907        for c in s.chars() {
1908            match c {
1909                '<' => result.push_str("&lt;"),
1910                '>' => result.push_str("&gt;"),
1911                '&' => result.push_str("&amp;"),
1912                _ => result.push(c),
1913            }
1914        }
1915        result
1916    }
1917}
1918
1919/// Escape special characters in XML attribute values.
1920fn escape_attribute_value(s: &str, preserve_entities: bool) -> String {
1921    if preserve_entities {
1922        escape_preserving_entities(s, true)
1923    } else {
1924        let mut result = String::with_capacity(s.len());
1925        for c in s.chars() {
1926            match c {
1927                '<' => result.push_str("&lt;"),
1928                '>' => result.push_str("&gt;"),
1929                '&' => result.push_str("&amp;"),
1930                '"' => result.push_str("&quot;"),
1931                '\'' => result.push_str("&apos;"),
1932                _ => result.push(c),
1933            }
1934        }
1935        result
1936    }
1937}
1938
1939/// Escape special characters while preserving entity references.
1940///
1941/// Recognizes entity reference patterns:
1942/// - Named entities: `&name;` (alphanumeric name)
1943/// - Decimal numeric entities: `&#digits;`
1944/// - Hexadecimal numeric entities: `&#xhex;` or `&#Xhex;`
1945fn escape_preserving_entities(s: &str, is_attribute: bool) -> String {
1946    let mut result = String::with_capacity(s.len());
1947    let chars: Vec<char> = s.chars().collect();
1948    let mut i = 0;
1949
1950    while i < chars.len() {
1951        let c = chars[i];
1952        match c {
1953            '<' => result.push_str("&lt;"),
1954            '>' => result.push_str("&gt;"),
1955            '"' if is_attribute => result.push_str("&quot;"),
1956            '\'' if is_attribute => result.push_str("&apos;"),
1957            '&' => {
1958                // Check if this is the start of an entity reference
1959                if let Some(entity_len) = try_parse_entity_reference(&chars[i..]) {
1960                    // It's a valid entity reference - copy it as-is
1961                    for j in 0..entity_len {
1962                        result.push(chars[i + j]);
1963                    }
1964                    i += entity_len;
1965                    continue;
1966                } else {
1967                    // Not a valid entity reference - escape the ampersand
1968                    result.push_str("&amp;");
1969                }
1970            }
1971            _ => result.push(c),
1972        }
1973        i += 1;
1974    }
1975
1976    result
1977}
1978
1979/// Try to parse an entity reference starting at the given position.
1980/// Returns the length of the entity reference if valid, or None if not.
1981///
1982/// Valid patterns:
1983/// - `&name;` where name is one or more alphanumeric characters
1984/// - `&#digits;` where digits are decimal digits
1985/// - `&#xhex;` or `&#Xhex;` where hex is hexadecimal digits
1986fn try_parse_entity_reference(chars: &[char]) -> Option<usize> {
1987    if chars.is_empty() || chars[0] != '&' {
1988        return None;
1989    }
1990
1991    // Need at least `&x;` (3 chars minimum)
1992    if chars.len() < 3 {
1993        return None;
1994    }
1995
1996    let mut len = 1; // Start after '&'
1997
1998    if chars[1] == '#' {
1999        // Numeric entity reference
2000        len = 2;
2001
2002        if len < chars.len() && (chars[len] == 'x' || chars[len] == 'X') {
2003            // Hexadecimal: &#xHEX;
2004            len += 1;
2005            let start = len;
2006            while len < chars.len() && chars[len].is_ascii_hexdigit() {
2007                len += 1;
2008            }
2009            // Need at least one hex digit
2010            if len == start {
2011                return None;
2012            }
2013        } else {
2014            // Decimal: &#DIGITS;
2015            let start = len;
2016            while len < chars.len() && chars[len].is_ascii_digit() {
2017                len += 1;
2018            }
2019            // Need at least one digit
2020            if len == start {
2021                return None;
2022            }
2023        }
2024    } else {
2025        // Named entity reference: &NAME;
2026        // Name must start with a letter or underscore, then letters, digits, underscores, hyphens, periods
2027        if !chars[len].is_ascii_alphabetic() && chars[len] != '_' {
2028            return None;
2029        }
2030        len += 1;
2031        while len < chars.len()
2032            && (chars[len].is_ascii_alphanumeric()
2033                || chars[len] == '_'
2034                || chars[len] == '-'
2035                || chars[len] == '.')
2036        {
2037            len += 1;
2038        }
2039    }
2040
2041    // Must end with ';'
2042    if len >= chars.len() || chars[len] != ';' {
2043        return None;
2044    }
2045
2046    Some(len + 1) // Include the semicolon
2047}
2048
2049/// Escape element name (for now, assume valid XML names).
2050fn escape_element_name(name: &str) -> &str {
2051    name
2052}