SOAP message objects
Build SOAP requests and read responses as Python objects - elements, attributes, lists and namespaces, with no schemas.
SOAPMessage represents the body of a SOAP request or response. Each attribute of the object corresponds to an XML element. The structure of a message is not declared in advance, and no schema or WSDL is required.
from zato.common.soap.message import SOAPMessage
request = SOAPMessage()
request.namespace = 'urn:cdc:iisb:2014'
request.hl7Message = 'MSH|^~\\&|...'
The preceding code constructs a complete request body. The following rules define how a SOAPMessage maps to XML.
Mapping rules
1. Attribute assignment creates elements
Assigning to an attribute creates an element. Intermediate elements spring into existence as you go deeper - there is nothing to declare first:
request = SOAPMessage()
request.order.customer.name = 'Jane Smith'
request.order.customer.city = 'Geneva'
2. Element order follows assignment order
Elements appear on the wire in exactly the order you assigned them. Nothing is reordered, which matters because many SOAP endpoints validate element order strictly - if the documentation of a system says username comes before password, assign them in that order and that is the order they will have.
3. Subscripts set XML attributes
Brackets set and read XML attributes:
One bracket key is reserved: namespace. Setting message.namespace picks the default namespace for the whole message, while setting it on a node overrides the namespace from that node down:
request = SOAPMessage()
request.namespace = 'urn:example:orders'
# This one subtree uses a different namespace
request.metadata['namespace'] = 'urn:example:common'
request.metadata.created_by = 'api'
4. Lists produce repeated elements
Assigning a list produces one element per item - both for scalars and for nested SOAPMessage nodes:
line1 = SOAPMessage()
line1.product = 'AA-100'
line1.quantity = 3
line2 = SOAPMessage()
line2.product = 'BB-200'
line2.quantity = 1
request.order.lines.line = [line1, line2]
<order>
<lines>
<line><product>AA-100</product><quantity>3</quantity></line>
<line><product>BB-200</product><quantity>1</quantity></line>
</lines>
</order>
5. Python types determine the XML representation
The type of the value dictates its lexical form on the wire - you assign natural Python values and the platform writes what the receiving system expects:
| You assign | The wire receives |
|---|---|
str | The text as-is |
int, float, Decimal | The number's canonical form |
bool | true or false |
datetime | An xs:dateTime timestamp |
date | An xs:date value |
None | An xsi:nil element |
bytes | An MTOM part when the connection has MTOM enabled, inline base64 otherwise |
6. Responses are read through attributes
Responses are SOAPMessage objects as well, so attributes read as they were assigned - repeated elements are lists, xsi:nil reads as None, and you never need to know or spell out the namespaces of the reply:
response = conn.invoke('getOrder', request)
order = response.getOrderResponse.order
status = order.status
for line in order.lines.line:
self.logger.info('%s x %s', line.product, line.quantity)
An element that has attributes is always a node - its attributes read through brackets and its text through str():
# <status code="42">Accepted</status>
code = response.status['code'] # '42'
text = str(response.status) # 'Accepted'
7. Responses expose protocol metadata
Two reserved attributes are added to responses when the connection uses the matching blocks:
response.addressing- the reply's WS-Addressing headers:action,message_id,relates_toresponse.attachments- the reply's MTOM parts, each withcontent_id,content_typeanddatabytes
Fault handling
A SOAP fault of either version is reported as a single exception type, SOAPFault, raised before any part of the response reaches your code:
from zato.common.soap.common import SOAPFault
try:
response = conn.invoke('submitSingleMessage', request)
except SOAPFault as fault:
self.logger.warning('Rejected: %s %s', fault.code, fault.reason)
fault.code and fault.reason are strings and fault.detail is a dot-accessed SOAPMessage holding whatever the endpoint put in the fault's detail section.