@@ -512,6 +512,146 @@ where
512512 }
513513}
514514
515+ /* XsiTypeSerializer */
516+
517+ /// A [`Serializer`] that wraps another (boxed) serializer to serialize a value
518+ /// of a dynamic type that is dispatched via the `xsi:type` attribute.
519+ ///
520+ /// The wrapped serializer emits the value using the element name of its actual
521+ /// (concrete) type. This serializer rewrites the resulting element so that it
522+ /// uses the dynamic type's base element `name` and moves the original element
523+ /// name into a `xsi:type` attribute instead. If the value is already using the
524+ /// base element name (i.e. it is the base type itself) no `xsi:type` attribute
525+ /// is added.
526+ #[ derive( Debug ) ]
527+ pub struct XsiTypeSerializer < ' ser , T > {
528+ inner : T ,
529+ name : & ' ser str ,
530+ is_root : bool ,
531+ state : XsiTypeState ,
532+ }
533+
534+ #[ derive( Debug ) ]
535+ enum XsiTypeState {
536+ /// The serializer was just created, no event was emitted yet.
537+ Init ,
538+
539+ /// The opening element was not emitted yet.
540+ Pending ,
541+
542+ /// The opening element was emitted, waiting for the matching closing
543+ /// element. The contained value is the current nesting depth.
544+ Open ( usize ) ,
545+
546+ /// The opening (and closing) element was emitted.
547+ Done ,
548+ }
549+
550+ impl < ' ser , T > XsiTypeSerializer < ' ser , T > {
551+ /// Create a new [`XsiTypeSerializer`] that wraps the passed `inner`
552+ /// serializer and rewrites the emitted element to use the passed base
553+ /// element `name`.
554+ #[ must_use]
555+ pub fn new ( inner : T , name : & ' ser str , is_root : bool ) -> Self {
556+ Self {
557+ inner,
558+ name,
559+ is_root,
560+ state : XsiTypeState :: Init ,
561+ }
562+ }
563+
564+ /// Rewrite the opening element so that it uses the base element name and
565+ /// references its original (concrete) name via a `xsi:type` attribute.
566+ fn rewrite ( & self , bytes : BytesStart < ' ser > , helper : & mut SerializeHelper ) -> BytesStart < ' ser > {
567+ // The value uses the base element name already, so it is the base type
568+ // and does not need a `xsi:type` attribute.
569+ if bytes. name ( ) . as_ref ( ) == self . name . as_bytes ( ) {
570+ return bytes;
571+ }
572+
573+ let type_name = bytes. name ( ) . as_ref ( ) . to_owned ( ) ;
574+
575+ let mut new = BytesStart :: new ( self . name ) ;
576+
577+ if self . is_root {
578+ helper. write_xmlns ( & mut new, Some ( & NamespacePrefix :: XSI ) , & Namespace :: XSI ) ;
579+ }
580+
581+ new. push_attribute ( ( & b"xsi:type" [ ..] , & type_name[ ..] ) ) ;
582+ new. extend_attributes ( bytes. attributes ( ) . filter_map ( |attr| {
583+ let attr = attr. ok ( ) ?;
584+
585+ if attr. key . as_ref ( ) == b"xsi:type" || attr. key . as_ref ( ) == b"xmlns:xsi" {
586+ None
587+ } else {
588+ Some ( attr)
589+ }
590+ } ) ) ;
591+
592+ new
593+ }
594+ }
595+
596+ impl < ' ser , T > Serializer < ' ser > for XsiTypeSerializer < ' ser , T >
597+ where
598+ T : Serializer < ' ser > ,
599+ {
600+ fn next ( & mut self , helper : & mut SerializeHelper ) -> Option < Result < Event < ' ser > , Error > > {
601+ let event = match self . inner . next ( helper) ? {
602+ Ok ( event) => event,
603+ Err ( error) => return Some ( Err ( error) ) ,
604+ } ;
605+
606+ if matches ! ( self . state, XsiTypeState :: Init ) {
607+ self . state = XsiTypeState :: Pending ;
608+ helper. begin_ns_scope ( ) ;
609+ }
610+
611+ if matches ! ( self . state, XsiTypeState :: Pending ) {
612+ return Some ( Ok ( match event {
613+ Event :: Empty ( bytes) => {
614+ let bytes = self . rewrite ( bytes, helper) ;
615+ self . state = XsiTypeState :: Done ;
616+
617+ Event :: Empty ( bytes)
618+ }
619+ Event :: Start ( bytes) => {
620+ let bytes = self . rewrite ( bytes, helper) ;
621+ self . state = XsiTypeState :: Open ( 1 ) ;
622+
623+ Event :: Start ( bytes)
624+ }
625+ other => {
626+ self . state = XsiTypeState :: Done ;
627+
628+ other
629+ }
630+ } ) ) ;
631+ }
632+
633+ if let XsiTypeState :: Open ( depth) = & mut self . state {
634+ match & event {
635+ Event :: Start ( _) => * depth += 1 ,
636+ Event :: End ( _) => {
637+ * depth -= 1 ;
638+
639+ if * depth == 0 {
640+ self . state = XsiTypeState :: Done ;
641+
642+ helper. end_ns_scope ( ) ;
643+
644+ return Some ( Ok ( Event :: End ( BytesEnd :: new ( self . name ) ) ) ) ;
645+ }
646+ }
647+ _ => ( ) ,
648+ }
649+ }
650+
651+ Some ( Ok ( event) )
652+ }
653+ }
654+
515655/// Trait for collecting namespaces of a type and all its sub-types.
516656///
517657/// Implement this trait to allow the dynamic serialization mode to discover which
0 commit comments