XML incorrecto producido por SUDS

Estoy tratando de hablar con un servicio web SOAP utilizando SUDS y Python. Después de un montón de problemas con el aprendizaje de Python (sí, soy nuevo en esto) y resolviendo cómo usar SUDS me he encontrado con un problema.

La firma del método web que estoy llamando, de acuerdo con la espuma, es

(FWTCaseCreate){ ClassificationEventCode = None Priority = None Title = None Description = None Queue = None DueDate = None AssociatedObject = (FWTObjectBriefDetails){ ObjectID = (FWTObjectID){ ObjectType = None ObjectReference[] =  } ObjectDescription = None Details = None Category = None } Form = (FWTCaseForm){ FormField[] =  FormName = None FormKey = None } Internal = None InteractionID = None XCoord = None YCoord = None } 

Así que uso SUDS para crear las clases que quiero y las envío al método. Sin embargo me sale un error. Así que encendí el inicio de sesión y puedo ver que el XML que se está enviando no es correcto, lo que está causando un error de deserialización. El paquete SOAP se parece a lo siguiente

     eaadf1ddff99a8      2000023 1 testing testing  True 356570 168708    

Como puede ver, hay un elemento ‘ClassificationEventCode’ alrededor de todos los demás elementos, esto no debería estar allí. Si corto y pego este xml en SOAPUI, primero elimino este elemento y luego lo publico directamente en el servicio web que funciona correctamente.

Aquí está el código que estoy usando para hacer la llamada

 client = Client(url) #Add a header for the security ssnns = ('wsse', 'http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd') ssn = Element('BinarySecurityToken', ns=ssnns).setText(binaryKey) ssn1 = Element('Security',ns=ssnns) ssn1.append(ssn) client.set_options(soapheaders=ssn1) newCase = client.factory.create('ns1:FWTCaseCreate') classEventCode = client.factory.create('ns1:FWTEventCode') classEventCode.value = 2000023 newCase.ClassificationEventCode = classEventCode newCase.Priority = 1 #optional newCase.AssociatedObject = None #optional newCase.Form = None #optional newCase.Internal = None #optional newCase.InteractionID = None #optional newCase.DueDate = None #optional newCase.Queue = None newCase.Title = 'Title' newCase.Description = 'description' newCase.XCoord = '356570' newCase.YCoord = '168708' caseID = client.service.createCase(newCase) 

¿Alguien tiene alguna idea de por qué esto está sucediendo? Supongo que SUDS cree que debería estar ahí según el WSDL.

Gracias.

Estaba recibiendo exactamente el mismo problema. La secuencia de parámetros en mi solicitud SOAP se está envolviendo en un elemento con el mismo nombre que el primer parámetro. p.ej

 ....    1 Mr ....    .... 

He revisado el WSDL para asegurarme de que no haya ningún problema con él.

El problema que parece es porque creé un objeto CreationReq usando el método client.factory.create. La comprobación del cliente mediante la impresión muestra que el método al que estoy llamando no toma ese objeto como parámetro. Más bien toma una lista de args nombrados.

Así que mi código era:

 req = client.factory.create('CreationReq') req.ReqType = 1 req.Title = 'Mr' resp = client.service.Create(req) 

Ahora es:

 req = {} req['ReqType'] = 1 req['Title'] = 'Mr' resp = client.service.Create(**req) 

Si crea un cliente para sus servicios de espuma, hay algunos atributos que puede ver para determinar qué objetos se necesitan para pasar a la llamada de servicio.

Por ejemplo:

 import suds client = suds.Client(url) for a in client.sd: #print the whole service definition print a 

Esto debería mostrarle los prefijos, puertos con métodos y tipos. Para su código, debe poder ver lo que se debe pasar en la llamada de servicio a createCase. Aunque el WSDL puede definir el método como que necesita un ‘FWTCaseCreate’, la espuma puede estar recogiendo la definición de createCase para necesitar ClassificationEventCode, Priority, Title types, etc.

Por lo tanto, no querría hacer: (que pasa en newCase para el primer argumento, poniendo todos los detalles bajo esa etiqueta)

 newCase = client.factory.create('ns1:FWTCaseCreate') caseID = client.service.createCase(newCase) 

Pero en lugar de eso, llame al servicio como tal: (basado en la definición del servicio)

 newCase = client.factory.create('ns1:FWTCaseCreate') caseID = client.service.createCase(newCase.ClassificationEventCode, newCase.Priority, ...) 

O tal vez:

 newCase = client.factory.create('ns1:FWTCaseCreate') caseID = client.service.createCase(*[getattr(newCase,a) for a in newCase.__keylist__]) 

Pasando en la lista de argumentos que se requieren para la llamada de servicio.

No sé por qué la definición de llamada de servicio se resuelve incorrectamente como lo que es, pero pasar el objeto correcto no se expande automáticamente a la lista de argumentos correcta necesaria. Tal vez leer la fuente de la espuma ( http://jortel.fedorapeople.org/suds/doc/ ) ayudaría a divulgar la respuesta.

¿Va a utilizar esto como un archivo de configuración, o para almacenar información. ¿O es esto para enviar datos a través de la web?

De acuerdo, si esto es así, ¿por qué no usar json o json-rpc? Se dice que son mucho más rápidos, más fáciles de analizar y mucho más fáciles de leer. XML es un tipo de datos horables y, personalmente, no puedo esperar a que muera, si vale la pena buscar el envío de datos, vale la pena utilizar los archivos de json.

Estás creando el elemento dos veces. Quita esto:

 classEventCode = client.factory.create('ns1:FWTEventCode') classEventCode.value = 2000023 

Y cambia esto:

 newcase.ClassificationEventCode = 2000023 

Esto debería eliminar esa etiqueta extra.

He encontrado este hilo buscando la solución del mismo problema. Hasta ahora he investigado que sucede solo cuando se pasa el objeto creado de fábrica directamente al método de servicio. Y solo con los tipos de datos wsdl usando extensión (herencia).

Hay más soluciones que podría pensar.

  • no utilice la fábrica para el tipo de nivel superior en absoluto.
  • escriba suds plugin cambiando xml después de generar
  • reescriba wsdl para no usar la herencia (etiqueta de extensión)
  • cambiar el tipo de objeto antes de pasar al método de servicio

He elegido la última, ya que es la forma más fácil. Así que ahí está el código.

 def sudsToDict(data): return dict([(str(key),val) for key,val in data]) 

Utilizar de esta manera

 data = client.factory.create('wsdl_data_type') # now fill with values and then data = sudsToDict(data) client.service.some_method(**data)