ValerioMC
ValerioMC

Reputation: 3166

Get simpleType with xsd:restriction using zeep (python)

I'm calling a SOAP Service and one of the elements i need to pass in my request is a simpleType defined with restriction enumeration value as follows:

<xsd:simpleType name="SAMPLE_SIMPLE_TYPE">
    <xsd:restriction base="xsd:string">
        <xsd:enumeration value="Code"/>
        <xsd:enumeration value="Service_Type_Code"/>
        <xsd:enumeration value="Description"/>
        <xsd:enumeration value="Print_Description"/>
        <xsd:enumeration value="Marketing_Starting_Date"/>
        <xsd:enumeration value="Marketing_Ending_Date"/>
    </xsd:restriction>
</xsd:simpleType>

I read the element using the following statement with zeep library

sample_simple_type = client.get_type("ns0:SAMPLE_SIMPLE_TYPE")

But unfortunately i cannot set any value as i usually do with other elements. (I mean i cannot set "Code", "Description", "Service_Type_Code" and so on)

It seems like i need to retrieve ns0:SAMPLE_SIMPLE_TYPE already with an enumerated value but i don't know how to do it.

Thanks

Upvotes: 1

Views: 1097

Answers (1)

Bogdan
Bogdan

Reputation: 24580

I think you may be overthinking this. That type might be a restriction, but fundamentally it's a string. It can't be any string, just the values in the enumeration, but nonetheless it's a string. So you can treat it as such.

Here is an example.

I have a mock web service running on http://localhost:8080/. It doesn't do much, it just acknowledges requests. The mock service has this interface:

<?xml version="1.0" encoding="utf-8"?>
<wsdl:definitions 
xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" 
xmlns:tns="http://tempuri.org/" 
xmlns:s="http://www.w3.org/2001/XMLSchema" 
xmlns:soap12="http://schemas.xmlsoap.org/wsdl/soap12/" 
targetNamespace="http://tempuri.org/" 
xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/">
  <wsdl:types>
    <s:schema elementFormDefault="qualified" targetNamespace="http://tempuri.org/">
    
    <s:simpleType name="SAMPLE_SIMPLE_TYPE">
        <s:restriction base="s:string">
            <s:enumeration value="Code" />
            <s:enumeration value="Service_Type_Code" />
            <s:enumeration value="Description" />
            <s:enumeration value="Print_Description" />
            <s:enumeration value="Marketing_Starting_Date" />
            <s:enumeration value="Marketing_Ending_Date" />
        </s:restriction>
    </s:simpleType>

    <s:element name="Request">
        <s:complexType>
          <s:sequence>
            <s:element minOccurs="1" maxOccurs="1" name="simpleString" type="s:string" />
            <s:element minOccurs="1" maxOccurs="1" name="yourEnumeration" type="tns:SAMPLE_SIMPLE_TYPE" />
          </s:sequence>
        </s:complexType>
    </s:element>
    
    <s:element name="Response">
        <s:complexType>
          <s:sequence>
            <s:element minOccurs="1" maxOccurs="1" name="ack" type="s:string" />
          </s:sequence>
        </s:complexType>
      </s:element>
    
    </s:schema>
  </wsdl:types>
  
  <wsdl:message name="TestSoapIn">
    <wsdl:part name="parameters" element="tns:Request" />
  </wsdl:message>
  
  <wsdl:message name="TestSoapOut">
    <wsdl:part name="parameters" element="tns:Response" />
  </wsdl:message>

  <wsdl:portType name="TestSoap">
    <wsdl:operation name="Request">
      <wsdl:input message="tns:TestSoapIn" />
      <wsdl:output message="tns:TestSoapOut" />
    </wsdl:operation>
  </wsdl:portType>

  <wsdl:binding name="TestSoap" type="tns:TestSoap">
    <soap:binding transport="http://schemas.xmlsoap.org/soap/http" />
    <wsdl:operation name="Request">
      <soap:operation soapAction="http://tempuri.org/test" style="document" />
      <wsdl:input>
        <soap:body use="literal" />
      </wsdl:input>
      <wsdl:output>
        <soap:body use="literal" />
      </wsdl:output>
    </wsdl:operation>
  </wsdl:binding>
  
  <wsdl:binding name="TestSoap12" type="tns:TestSoap">
    <soap12:binding transport="http://schemas.xmlsoap.org/soap/http" />
    <wsdl:operation name="Request">
      <soap12:operation soapAction="http://tempuri.org/test" style="document" />
      <wsdl:input>
        <soap12:body use="literal" />
      </wsdl:input>
      <wsdl:output>
        <soap12:body use="literal" />
      </wsdl:output>
    </wsdl:operation>
  </wsdl:binding>
  
  <wsdl:service name="Test">
    <wsdl:port name="TestSoap" binding="tns:TestSoap">
      <soap:address location="http://localhost:8080/" />
    </wsdl:port>
    <wsdl:port name="TestSoap12" binding="tns:TestSoap12">
      <soap12:address location="http://localhost:8080/" />
    </wsdl:port>
  </wsdl:service>

</wsdl:definitions>

I've added your type to it.

If I run:

python -mzeep http://localhost:8080/test.wsdl

I get the following method definition from zeep:

...
...
Bindings:
     Soap11Binding: {http://tempuri.org/}TestSoap
     Soap12Binding: {http://tempuri.org/}TestSoap12

Service: Test
     Port: TestSoap (Soap11Binding: {http://tempuri.org/}TestSoap)
         Operations:
            Request(simpleString: xsd:string, yourEnumeration: ns0:SAMPLE_SIMPLE_TYPE) -> ack: xsd:string

     Port: TestSoap12 (Soap12Binding: {http://tempuri.org/}TestSoap12)
         Operations:
            Request(simpleString: xsd:string, yourEnumeration: ns0:SAMPLE_SIMPLE_TYPE) -> ack: xsd:string

Now see this code:

from zeep import Client
from zeep.plugins import HistoryPlugin
from lxml import etree

def print_history(h):
    print(etree.tostring(h.last_sent["envelope"], encoding = "unicode", pretty_print = True))
    print(etree.tostring(h.last_received["envelope"], encoding = "unicode", pretty_print = True))

history = HistoryPlugin()
client = Client('http://localhost:8080/test.wsdl', plugins = [history]) 
    
client.service.Request('whatever', 'Service_Type_Code')
print_history(history)

print('--------------------------------\n')

client.service.Request('whatever', 'bla bla bla')
print_history(history)

print('--------------------------------\n')

sample_simple_type = client.get_type("ns0:SAMPLE_SIMPLE_TYPE")

client.service.Request('whatever', sample_simple_type('Service_Type_Code'))
print_history(history)

print('--------------------------------\n')

client.service.Request('whatever', sample_simple_type('bla bla bla'))
print_history(history)

print('--------------------------------\n')

print(help(sample_simple_type))

It will produce the following output:

<soap-env:Envelope xmlns:soap-env="http://schemas.xmlsoap.org/soap/envelope/">
  <soap-env:Body>
    <ns0:Request xmlns:ns0="http://tempuri.org/">
      <ns0:simpleString>whatever</ns0:simpleString>
      <ns0:yourEnumeration>Service_Type_Code</ns0:yourEnumeration>
    </ns0:Request>
  </soap-env:Body>
</soap-env:Envelope>

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:tem="http://tempuri.org/">
   <soapenv:Header/>
   <soapenv:Body>
      <tem:Response>
         <tem:ack>ok</tem:ack>
      </tem:Response>
   </soapenv:Body>
</soapenv:Envelope>

--------------------------------

<soap-env:Envelope xmlns:soap-env="http://schemas.xmlsoap.org/soap/envelope/">
  <soap-env:Body>
    <ns0:Request xmlns:ns0="http://tempuri.org/">
      <ns0:simpleString>whatever</ns0:simpleString>
      <ns0:yourEnumeration>bla bla bla</ns0:yourEnumeration>
    </ns0:Request>
  </soap-env:Body>
</soap-env:Envelope>

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:tem="http://tempuri.org/">
   <soapenv:Header/>
   <soapenv:Body>
      <tem:Response>
         <tem:ack>ok</tem:ack>
      </tem:Response>
   </soapenv:Body>
</soapenv:Envelope>

--------------------------------

<soap-env:Envelope xmlns:soap-env="http://schemas.xmlsoap.org/soap/envelope/">
  <soap-env:Body>
    <ns0:Request xmlns:ns0="http://tempuri.org/">
      <ns0:simpleString>whatever</ns0:simpleString>
      <ns0:yourEnumeration>Service_Type_Code</ns0:yourEnumeration>
    </ns0:Request>
  </soap-env:Body>
</soap-env:Envelope>

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:tem="http://tempuri.org/">
   <soapenv:Header/>
   <soapenv:Body>
      <tem:Response>
         <tem:ack>ok</tem:ack>
      </tem:Response>
   </soapenv:Body>
</soapenv:Envelope>

--------------------------------

<soap-env:Envelope xmlns:soap-env="http://schemas.xmlsoap.org/soap/envelope/">
  <soap-env:Body>
    <ns0:Request xmlns:ns0="http://tempuri.org/">
      <ns0:simpleString>whatever</ns0:simpleString>
      <ns0:yourEnumeration>bla bla bla</ns0:yourEnumeration>
    </ns0:Request>
  </soap-env:Body>
</soap-env:Envelope>

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:tem="http://tempuri.org/">
   <soapenv:Header/>
   <soapenv:Body>
      <tem:Response>
         <tem:ack>ok</tem:ack>
      </tem:Response>
   </soapenv:Body>
</soapenv:Envelope>

--------------------------------

Help on SAMPLE_SIMPLE_TYPE object:

class SAMPLE_SIMPLE_TYPE(zeep.xsd.types.builtins.String)
 |  SAMPLE_SIMPLE_TYPE(qname=None, is_global=False)
 |  
 |  Method resolution order:
 |      SAMPLE_SIMPLE_TYPE
 |      zeep.xsd.types.builtins.String
 |      zeep.xsd.types.builtins.BuiltinType
 |      zeep.xsd.types.simple.AnySimpleType
 |      zeep.xsd.types.any.AnyType
 |      zeep.xsd.types.base.Type
 |      builtins.object
 |  
 |  Methods inherited from zeep.xsd.types.builtins.String:
 |  
 |  pythonvalue(self, value)
 |  
 |  xmlvalue = _wrapper(self, value)
 |  
 |  ----------------------------------------------------------------------
 |  Data and other attributes inherited from zeep.xsd.types.builtins.String:
 |  
 |  accepted_types = [<class 'str'>]
 |  
 |  ----------------------------------------------------------------------
 |  Methods inherited from zeep.xsd.types.builtins.BuiltinType:
 |  
 |  __init__(self, qname=None, is_global=False)
 |      Initialize self.  See help(type(self)) for accurate signature.
 |  
 |  ----------------------------------------------------------------------
 |  Methods inherited from zeep.xsd.types.simple.AnySimpleType:
 |  
 |  __call__(self, *args, **kwargs)
 |      Return the xmlvalue for the given value.
 |      
 |      Expects only one argument 'value'.  The args, kwargs handling is done
 |      here manually so that we can return readable error messages instead of
 |      only '__call__ takes x arguments'
 |  
 |  __eq__(self, other)
 |      Return self==value.
 |  
 |  __str__(self)
 |      Return str(self).
 |  
 |  parse_xmlelement(self, xmlelement: lxml.etree._Element, schema: 'Schema' = None, allow_none: bool = True, context: zeep.xsd.context.XmlParserContext = None, schema_type: 'Type' = None) -> Union[str, zeep.xsd.valueobjects.CompoundValue, List[lxml.etree._Element], NoneType]
 |      Try to parse the xml element and return a value for it.
 |      
 |      There is a big chance that we cannot parse this value since it is an
 |      Any. In that case we just return the raw lxml Element nodes.
 |      
 |      :param xmlelement: XML element objects
 |      :param schema: The parent XML schema
 |      :param allow_none: Allow none
 |      :param context: Optional parsing context (for inline schemas)
 |      :param schema_type: The original type (not overriden via xsi:type)
 |  
 |  render(self, node: lxml.etree._Element, value: Union[list, dict, zeep.xsd.valueobjects.CompoundValue], xsd_type: 'ComplexType' = None, render_path=None) -> None
 |  
 |  signature(self, schema=None, standalone=True)
 |  
 |  validate(self, value, required=False)
 |  
 |  ----------------------------------------------------------------------
 |  Data and other attributes inherited from zeep.xsd.types.simple.AnySimpleType:
 |  
 |  __hash__ = None
 |  
 |  ----------------------------------------------------------------------
 |  Methods inherited from zeep.xsd.types.any.AnyType:
 |  
 |  resolve(self)
 |  
 |  ----------------------------------------------------------------------
 |  Methods inherited from zeep.xsd.types.base.Type:
 |  
 |  accept(self, value)
 |  
 |  extend(self, child)
 |  
 |  get_prefixed_name(self, schema)
 |  
 |  parse_kwargs(self, kwargs, name, available_kwargs)
 |  
 |  parsexml(self, xml, schema=None)
 |  
 |  restrict(self, child)
 |  
 |  ----------------------------------------------------------------------
 |  Readonly properties inherited from zeep.xsd.types.base.Type:
 |  
 |  attributes
 |  
 |  ----------------------------------------------------------------------
 |  Data descriptors inherited from zeep.xsd.types.base.Type:
 |  
 |  __dict__
 |      dictionary for instance variables (if defined)
 |  
 |  __weakref__
 |      list of weak references to the object (if defined)

None

As you can see, I can treat that parameter as a string because it is a string. From the code, you can see that zeep doesn't even validate that the values are from within the enumeration (at least for now). That's why 'bla bla bla' also works.

The restriction is something that the server should enforce because it's part of its contract. If you send anything else than a value from the enumeration, you should get back a SoapFault. Since my service is just a dumb mock, it does no validation either, it just says it received a request.

Upvotes: 1

Related Questions