Suds genera elementos vacíos; ¿Cómo eliminarlos?

[Edición principal basada en la experiencia desde la primera publicación hace dos días.]

Estoy creando un script Python SOAP / XML usando Suds, pero estoy luchando por obtener el código para generar SOAP / XML que sea aceptable para el servidor. Pensé que el problema era que Suds no generaba prefijos para elementos internos, pero posteriormente resulta que la falta de prefijos (verSh-Data y elementos internos) no es un problema, ya queSh-Data yMetaSwitchData elementos declaran espacios de nombres apropiados (ver más abajo).

<SOAP-ENV:Envelope xmlns:ns3="http://www.metaswitch.com/ems/soap/sh" xmlns:ns0="http://www.metaswitch.com/ems/soap/sh/userdata" xmlns:ns1="http://www.metaswitch.com/ems/soap/sh/servicedata" xmlns:ns2="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
   <SOAP-ENV:Header/>
   <ns2:Body>
      <ns3:ShUpdate>
         <ns3:UserIdentity>Meribel/TD Test Sub Gateway 3</ns3:UserIdentity>
         <ns3:DataReference>0</ns3:DataReference>
         <ns3:UserData>
            <Sh-Data xmlns="http://www.metaswitch.com/ems/soap/sh/userdata">
               <RepositoryData>
                  <ServiceIndication>Meta_SubG_BaseInformation</ServiceIndication>
                  <SequenceNumber>0</SequenceNumber>
                  <ServiceData>
                     <MetaSwitchData xmlns="http://www.metaswitch.com/ems/soap/sh/servicedata" IgnoreSequenceNumber="False" MetaSwitchVersion="?">
                        <Meta_SubG_BaseInformation Action="apply">
                           <NetworkElementName>Meribel</NetworkElementName>
                           <Description>TD Test Sub Gateway 3</Description>
                           <DomainName>test.datcon.co.uk</DomainName>
                           <MediaGatewayModel>Cisco ATA</MediaGatewayModel>
                           <CallFeatureServerControlStatus/>
                           <CallAgentControlStatus/>
                           <UseStaticNATMapping/>
                           <AuthenticationRequired/>
                           <ProviderStatus/>
                           <DeactivationMode/>
                        </Meta_SubG_BaseInformation>
                     </MetaSwitchData>
                  </ServiceData>
               </RepositoryData>
            </Sh-Data>
         </ns3:UserData>
         <ns3:OriginHost>[email protected]?clientVersion=7.3</ns3:OriginHost>
      </ns3:ShUpdate>
   </ns2:Body>
</SOAP-ENV:Envelope>

Pero esto todavía falla. El problema es que Suds genera elementos vacíos para elementos opcionales (marcados comoMandatory = No en el WSDL). Pero el servidor requiere que un elemento opcional esté presente con un valor sensible o ausente, y obtengo el siguiente error (porque el<CallFeatureServerControlStatus/> element no es uno de los valores permitidos.

Los datos de usuario proporcionados no se validaron con el Esquema XML de MetaSwitch para datos de usuario.
Details: cvc-enumeration-valid: el valor '' no es válido con respecto a la enumeración '[Control, Abandoned, Cautiously control]'. Debe ser un valor de la enumeración.

Si tomo el SOAP / XML generado en SOAPUI y elimino los elementos vacíos, la solicitud funciona bien.

¿Hay alguna forma de hacer que Suds no genere elementos vacíos para campos opcionales o que luego los elimine en el código?

Actualización principal

He resuelto este problema (que he visto en otros lugares) pero de una manera bastante poco elegante. Así que publico mi solución actual con la esperanza de que a) ayude a otros y / o b) alguien pueda sugerir una mejor solución.

Resulta que el problema no era que Suds genera elementos vacíos para elementos opcionales (marcados comoMandatory = No en el WSDL). Sino que Suds genera elementos vacíos para @ opcioncomplej elementos. Por ejemplo, los siguientes elementos Meta_SubG_BaseInformation son elementos simples y Suds no genera nada para ellos en SOAP / XML.

<xs:element name="CMTS" type="xs:string" minOccurs="0">
    <xs:annotation>
        <xs:documentation>
            <d:DisplayName firstVersion="5.0" lastVersion="7.4">CMTS</d:DisplayName>
            <d:ValidFrom>5.0</d:ValidFrom>
            <d:ValidTo>7.4</d:ValidTo>
            <d:Type firstVersion="5.0" lastVersion="7.4">String</d:Type>
            <d:BaseAccess firstVersion="5.0" lastVersion="7.4">RWRWRW</d:BaseAccess>
            <d:Mandatory firstVersion="5.0" lastVersion="7.4">No</d:Mandatory>
            <d:MaxLength firstVersion="5.0" lastVersion="7.4">1024</d:MaxLength>
        </xs:documentation>
    </xs:annotation>
</xs:element>

<xs:element name="TAGLocation" type="xs:string" minOccurs="0">
    <xs:annotation>
        <xs:documentation>
            <d:DisplayName>Preferred location of Trunk Gateway</d:DisplayName>
            <d:Type>String</d:Type>
            <d:BaseAccess>RWRWRW</d:BaseAccess>
            <d:Mandatory>No</d:Mandatory>
            <d:DefaultValue>None</d:DefaultValue>
            <d:MaxLength>1024</d:MaxLength>
        </xs:documentation>
    </xs:annotation>
</xs:element>

En contraste, el siguiente elemento Meta_SubG_BaseInformation es un elemento complejo, e incluso cuando es opcional y mi código no le asigna un valor, termina en el SOAP / XML generado.

<xs:element name="ProviderStatus" type="tMeta_SubG_BaseInformation_ProviderStatus" minOccurs="0">
    <xs:annotation>
        <xs:documentation>
            <d:DisplayName>Provider status</d:DisplayName>
            <d:Type>Choice of values</d:Type>
            <d:BaseAccess>R-R-R-</d:BaseAccess>
            <d:Mandatory>No</d:Mandatory>
            <d:Values>
                <d:Value>Unavailable</d:Value>
                <d:Value>Available</d:Value>
                <d:Value>Inactive</d:Value>
                <d:Value>Active</d:Value>
                <d:Value>Out of service</d:Value>
                <d:Value>Quiescing</d:Value>
                <d:Value>Unconfigured</d:Value>
                <d:Value>Pending available</d:Value>
            </d:Values>
        </xs:documentation>
    </xs:annotation>
</xs:element>

Suds genera lo siguiente para ProviderStatus que (como se indicó anteriormente) altera mi servidor.

<ProviderStatus/>

La solución alternativa es configurar todas lasMeta_SubG_BaseInformation elementos aNone después de crear el elemento padre y antes de asignar valores, como se muestra a continuación. Esto es superfluo para los elementos simples, pero asegura que los elementos complejos no asignados no generen SOAP / XML generado.

subGatewayBaseInformation = client.factory.create('ns1:Meta_SubG_BaseInformation')
for (el) in subGatewayBaseInformation:
  subGatewayBaseInformation.__setitem__(el[0], None)
subGatewayBaseInformation._Action            = 'apply'
subGatewayBaseInformation.NetworkElementName = 'Meribel'
etc...

Esto da como resultado que Suds genere SOAP / XML sin elementos vacíos, lo cual es aceptable para mi servidor.

¿Pero alguien sabe de una forma más limpia de lograr el mismo efecto?

La solución a continuación se basa en las respuestas / comentarios de dusan y Roland Smith a continuación.

Esta solución utiliza un Suds MessagePlugin para podar XML "vacío" del formulario<SubscriberType/> antes de que Suds ponga la solicitud en el cable. Solo necesitamos podar en ShUpdates (donde estamos actualizando datos en el servidor), y la lógica (especialmente la indexación en los hijos para obtener la lista de elementos de indicación de servicio) es muy específica para el WSDL. No funcionaría para WSDL diferente.

class MyPlugin(MessagePlugin):
  def marshalled(self, context):
    pruned = []
    req = context.envelope.children[1].children[0]
    if (req.name == 'ShUpdate'):
      si = req.children[2].children[0].children[0].children[2].children[0].children[0]
      for el in si.children:
        if re.match('<[a-zA-Z0-9]*/>', Element.plain(el)):
          pruned.append(el)
      for p in pruned:
        si.children.remove(p)

Y luego solo necesitamos hacer referencia al complemento cuando creamos el cliente.

client = Client(url, plugins=[MyPlugin()])

Respuestas a la pregunta(14)

Su respuesta a la pregunta