2012-04-18 4 views
5

Ich benutze Python/Suds, um einen Client zu implementieren, und ich bekomme falsche Namespacepräfixe in der gesendeten SOAP-Header für eine spezifische Art von Parametern von element ref= in der WSDL definiert.Python verschmutzt falsche Namespacepräfix in SOAP-Anfrage

Die .wsdl verweist auf eine .xsd-Datei mit Datentypen, siehe unten. Das Problem tritt mit der Funktion GetRecordAttributes und ihrem ersten Argument vom Typ gbt:recordReferences auf.

Datei: browse2.wsdl

<xsd:schema targetNamespace="http://www.grantadesign.com/10/10/Browse" xmlns="http://www.grantadesign.com/10/10/Browse" xmlns:gbt="http://www.grantadesign.com/10/10/GrantaBaseTypes" elementFormDefault="qualified" attributeFormDefault="qualified"> 
<xsd:import schemaLocation="grantabasetypes2.xsd" namespace="http://www.grantadesign.com/10/10/GrantaBaseTypes"/> 
<xsd:element name="GetRecordAttributes"> 
     <xsd:complexType> 
      <xsd:sequence> 
       <xsd:element ref="gbt:recordReferences"> 
       </xsd:element> 

referenzierte Datei: grantabasetypes2.xsd

<element name="recordReferences"> 
    <complexType> 
    <sequence> 
     <element name="record" minOccurs="0" maxOccurs="unbounded" type="gbt:MIRecordReference"/> 
    </sequence> 
    </complexType> 
</element> 

SOAP-Anforderung von Schaum gesendet:

<SOAP-ENV:Envelope xmlns:ns0="http://www.grantadesign.com/10/10/GrantaBaseTypes" xmlns:ns1="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns2="http://www.grantadesign.com/10/10/Browse" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"> 
    <SOAP-ENV:Header/> 
    <ns1:Body> 
     <ns2:GetRecordAttributes> 
     <ns2:recordReferences> 
      <ns0:record> 
      </ns0:record> 
     </ns2:recordReferences> 
     </ns2:GetRecordAttributes> 
    </ns1:Body> 
</SOAP-ENV:Envelope> 

Problem: <ns2:recordReferences> hat ein falsches Präfix, sollte <ns0:recordReferences> sein, da es zum Namespace ...GrantaBaseTypes gehört, der in der .xsd definiert ist.

Dies geschieht für alle Argumente, die von ref= in der WSDL definiert sind. Wie kann dies automatisch behoben werden?

Hinweis: Ich habe überprüft, dass das Präfix "good" vom Dienst akzeptiert wird, indem die xml-SOAP-Anforderung manuell über curl gesendet wird.

UPDATE

ich mit SUDS Quellcode und dem folgenden empirischen fix Kräften alle Elemente mit ref= Attribute einmischte die Referee-ed-Namespace zu übernehmen (zuvor, nehmen sie auf dem Schemastammnamespace oder was auch immer tns ist) :

Datei: /suds/xsd/sxbase.py

class SchemaObject(object): 
.... 
    def namespace(self, prefix=None): 

     ns = self.schema.tns 

#FIX BEGIN 
     if self.ref and self.ref in self.schema.elements.keys(): 
      ns = self.ref 
#FIX END 

Arbeiten mit meinem Service, aber ich bin nicht sicher, ob es andere Sache brechen würde s. Ich würde eine intelligentere Lösung bevorzugen, die SUDS Quellcode nicht ändert.

Danke,

Alex

+0

Dies ist eindeutig ein Fehler in Ihrem Stapel; Was meinst du mit "automatisch"? Sind Sie beispielsweise bereit, mit einer anderen, gleichwertigen XSD zu arbeiten, die mit Ihren Werkzeugen zusammenarbeitet? Von dem, was Sie sagen, wenn Sie die Ref mit einem lokal definierten Element ersetzen würden, wird es funktionieren; Bei allen XSD-Problemen ist das generierte XML identisch. Wenn Sie darüber nachdenken, den Flug zu ändern, zum Beispiel mit einer Art XSLT über einen Proxy, wäre das ein anderer Ansatz. Ich könnte eine Lösung empfehlen, die Ihre XSD automatisch refaktorisiert, um Refs durch lokale Elemente zu ersetzen. –

+0

Ich habe tatsächlich versucht, Definitionen zu verschieben und zu ändern, aber da ich kein Experte bin, konnte ich vielleicht nicht die richtige Syntax bestimmen, SUDS hat immer den falschen Namespace dort hingelegt. Ich möchte auch nicht mit diesen spielen, da sie von einem externen Anbieter zur Verfügung gestellt werden und Änderungen unterliegen. Siehe UPDATE für eine nicht optimale Lösung, die ich bisher gefunden habe. –

Antwort

8

ein Suds plugin Schreiben Sie die XML zu ändern, bevor es gesendet wird.

from suds.client import Client 
from suds.plugin import MessagePlugin 

class MyPlugin(MessagePlugin): 
    def marshalled(self, context): 
     #modify this line to reliably find the "recordReferences" element 
     context.envelope[1][0][0].setPrefix('ns0') 

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

Zitiert Suds Dokumentation:

vermarshallten()
Bietet das Plugin die Möglichkeit, den Umschlag Dokument zu prüfen/ändern, bevor es gesendet wird.

+1

Dies funktioniert in diesem speziellen Fall gut. Da es jedoch viele solcher Parameter an verschiedenen Baumpositionen gibt, ist das Fixieren von Hand nicht optimal. Ich möchte etwas wie _prefix ns0 zu allen Elementen mit ref = _, und während man wohl den ganzen xml-Baum parsen könnte, glaube ich, dass context.envelope nicht die 'ref =' Information behält. –

+0

Eine weitere Option ist das Erweitern von 'DocumentPlugin', um das WSDL/XSD-Objekt zu modifizieren. Die Methode' parsed() '(siehe Dokumentation) wird zweimal aufgerufen (eine für die WSDL, eine andere für die XSD). – dusan

+0

Brilliant, danke !! Interessanterweise musste ich den Umschlag an genau der gleichen Stelle (strukturell) korrigieren wie das ursprüngliche Beispiel. Gibt es hier ein Muster? – Gesias

1

Sie könnten sich Seife Nachricht erstellen und verwenden SoapClient die Nachricht zu senden:

sc = SoapClient(cli.service.XXXMethod.client,cli.service.XXXMethod.method) 
sc.send(some_soap_doc) 
2

Ich hatte genau das gleiche Problem, wenn Schaum mit einem BizTalk/IIS SOAP-Dienst zuzugreifen. Von dem, was ich von der WSDL erkennen kann, tritt es auf, wenn es einen "complexType" gibt, der nicht Teil des "targetNamespace" ist (es hat einen eigenen), der auch einen complexType, aber keinen Namespace hat. In BizTalk bedeutet dies, dass das Kind zum selben Namespace wie das Elternteil gehören sollte, aber Suds scheinen zu denken, dass es dann Teil des targetNamespace .... sein sollte.

Die Korrektur im Quellcode löste das Ding " korrekt ", aber da ich in der Lage sein möchte, ohne die Anwendung jedes Mal zu aktualisieren, ging ich für eine andere Lösung ....

Meine Lösung war es, Suds zu überspringen und nur das rohe XML zu kopieren, verwenden Sie das als Vorlage und kopiere die Werte hinein ... Nicht schön, aber zumindest einfach. Die Lösung, um ein Plugin hinzuzufügen, ist meiner Meinung nach genauso hart codiert und vielleicht sogar schwerer zu warten.

0

Ich ziehe es reguläre Ausdrücke :)

import re 

class EnvelopeFixer(MessagePlugin): 
    def sending(self, context): 
     # rimuovi i prefissi 
     context.envelope = re.sub('ns[0-9]:', '', context.envelope) 
     return context.envelope 
+0

Ihre Methode funktioniert nicht sehr gut: WebFault: Server ausgelöst Fehler: 'Das Stammelement für die Anfrage konnte nicht ermittelt werden. – Allen

+0

mmhhh ... so ist die Plugin-Strategie der Weg ... –