@@ -512,6 +512,121 @@ 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+ state : XsiTypeState ,
531+ }
532+
533+ #[ derive( Debug ) ]
534+ enum XsiTypeState {
535+ /// The opening element was not emitted yet.
536+ Pending ,
537+
538+ /// The opening element was emitted, waiting for the matching closing
539+ /// element. The contained value is the current nesting depth.
540+ Open ( usize ) ,
541+
542+ /// The opening (and closing) element was emitted.
543+ Done ,
544+ }
545+
546+ impl < ' ser , T > XsiTypeSerializer < ' ser , T > {
547+ /// Create a new [`XsiTypeSerializer`] that wraps the passed `inner`
548+ /// serializer and rewrites the emitted element to use the passed base
549+ /// element `name`.
550+ #[ must_use]
551+ pub fn new ( inner : T , name : & ' ser str ) -> Self {
552+ Self {
553+ inner,
554+ name,
555+ state : XsiTypeState :: Pending ,
556+ }
557+ }
558+
559+ /// Rewrite the opening element so that it uses the base element name and
560+ /// references its original (concrete) name via a `xsi:type` attribute.
561+ fn rewrite ( & self , bytes : BytesStart < ' ser > ) -> BytesStart < ' ser > {
562+ // The value uses the base element name already, so it is the base type
563+ // and does not need a `xsi:type` attribute.
564+ if bytes. name ( ) . as_ref ( ) == self . name . as_bytes ( ) {
565+ return bytes;
566+ }
567+
568+ let type_name = bytes. name ( ) . as_ref ( ) . to_owned ( ) ;
569+
570+ let mut new = BytesStart :: new ( self . name ) ;
571+ new. push_attribute ( ( & b"xsi:type" [ ..] , & type_name[ ..] ) ) ;
572+ new. extend_attributes ( bytes. attributes ( ) . filter_map ( Result :: ok) ) ;
573+
574+ new
575+ }
576+ }
577+
578+ impl < ' ser , T > Serializer < ' ser > for XsiTypeSerializer < ' ser , T >
579+ where
580+ T : Serializer < ' ser > ,
581+ {
582+ fn next ( & mut self , helper : & mut SerializeHelper ) -> Option < Result < Event < ' ser > , Error > > {
583+ let event = match self . inner . next ( helper) ? {
584+ Ok ( event) => event,
585+ Err ( error) => return Some ( Err ( error) ) ,
586+ } ;
587+
588+ if matches ! ( self . state, XsiTypeState :: Pending ) {
589+ return Some ( Ok ( match event {
590+ Event :: Empty ( bytes) => {
591+ let bytes = self . rewrite ( bytes) ;
592+ self . state = XsiTypeState :: Done ;
593+
594+ Event :: Empty ( bytes)
595+ }
596+ Event :: Start ( bytes) => {
597+ let bytes = self . rewrite ( bytes) ;
598+ self . state = XsiTypeState :: Open ( 1 ) ;
599+
600+ Event :: Start ( bytes)
601+ }
602+ other => {
603+ self . state = XsiTypeState :: Done ;
604+
605+ other
606+ }
607+ } ) ) ;
608+ }
609+
610+ if let XsiTypeState :: Open ( depth) = & mut self . state {
611+ match & event {
612+ Event :: Start ( _) => * depth += 1 ,
613+ Event :: End ( _) => {
614+ * depth -= 1 ;
615+
616+ if * depth == 0 {
617+ self . state = XsiTypeState :: Done ;
618+
619+ return Some ( Ok ( Event :: End ( BytesEnd :: new ( self . name ) ) ) ) ;
620+ }
621+ }
622+ _ => ( ) ,
623+ }
624+ }
625+
626+ Some ( Ok ( event) )
627+ }
628+ }
629+
515630/// Trait for collecting namespaces of a type and all its sub-types.
516631///
517632/// Implement this trait to allow the dynamic serialization mode to discover which
0 commit comments