Python-zeep: Arrays in the Request

Created on 23 Jul 2016  路  17Comments  路  Source: mvantellingen/python-zeep

I'm having problems formatting a request that contains an array of input messages... when the renderer hits an array it doesn't add each element of the array to the soap message - and it throws an exception because the sub_value is None... getattr() returns None because it's looking for the element name - but the value is an array. I'm working with the types and elements source trying to debug it - not sure if it's a problem with the code or with the way I'm building the request message.

Exception:

<type 'exceptions.TypeError'>, TypeError("'NoneType' object is not iterable",), None

At: elements.py:

   def render(self, parent, value):
          for val in value:
            node = etree.SubElement(parent, self.qname)
            self.type.render(node, val)

Code I'm using to populate the soap request:

        api_client = Client(api_address_setting.setting_value + 'GetEntityWebService.asmx?WSDL')
        search_parameter_type = api_client.get_type('ns1:EntitySearchParameter')
        search_parameter = search_parameter_type()
        search_parameter.FieldName = 'Id'
        search_parameter.FieldValue = '0'
        search_parameter.Operator = 'GreaterThan'
        entity_search_clause_type = api_client.get_type('ns1:EntitySearchClause')
        entity_search_clause = entity_search_clause_type()
        entity_search_clause.Operator = 'And'
        entity_search_clause.SearchParameters = []
        entity_search_clause.SearchParameters.append(search_parameter)
        entity_request_type = api_client.get_type('ns1:EntityRequest')
        entity_request = entity_request_type()
        entity_request.EntityType = 'Staff'
        entity_request.EntitySearchClauses = [];
        entity_request.EntitySearchClauses.append(entity_search_clause)
        entity_request.RequestId = 0
        entity_msg_type = api_client.get_type('ns1:GetEntityInMsg')
        entity_msg = entity_msg_type()
        entity_msg.CorrelationId = -1
        entity_msg.CustomAttributes = []
        entity_msg.Entity = entity_request
        entity_msg.MessageId = -1
        entity_msg.RowCount = 0
        entity_msg.SkipSemanticValidation = False
        staff_req_type = api_client.get_type('ns1:GetEntityRequest')
        staff_req = staff_req_type()
        staff_req.TokenId = tokenId
        staff_req.Entities = []
        staff_req.Entities.append(entity_msg)
        staff_result = api_client.service.GetEntity(staff_req)

Sample soap message that works (from a .net client):

<GetEntityRequest xmlns="http://www.companyname.com/Soa/Foundation/MessageDefinition.xsd">
  <TokenId>2KVFvpE... etc...</TokenId>
  <Entities>
    <GetEntityInMsg>
      <Entity>
        <RequestId>0</RequestId>
        <EntityType>Staff</EntityType>
        <EntitySearchClauses>
          <SearchParameters>
            <FieldName>Id</FieldName>
            <FieldValue>0</FieldValue>
            <Operator>GreaterThan</Operator>
          </SearchParameters>
          <Operator>And</Operator>
        </EntitySearchClauses>
      </Entity>
    </GetEntityInMsg>
    <GetEntityInMsg>
      <Entity>
        <RequestId>1</RequestId>
        <EntityType>Campus</EntityType>
        <EntitySearchClauses>
          <SearchParameters>
            <FieldName>Id</FieldName>
            <FieldValue>0</FieldValue>
            <Operator>GreaterThan</Operator>
          </SearchParameters>
          <Operator>And</Operator>
        </EntitySearchClauses>
      </Entity>
    </GetEntityInMsg>
  </Entities>
</GetEntityRequest>

Here's the wsdl:

<?xml version="1.0" encoding="utf-8"?>
<wsdl:definitions xmlns:s="http://www.w3.org/2001/XMLSchema" xmlns:soap12="http://schemas.xmlsoap.org/wsdl/soap12/" xmlns:http="http://schemas.xmlsoap.org/wsdl/http/" xmlns:mime="http://schemas.xmlsoap.org/wsdl/mime/" xmlns:tns="http://www.company.com/Soa/Foundation/" xmlns:s1="http://www.company.com/Soa/Foundation/MessageDefinition.xsd" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:tm="http://microsoft.com/wsdl/mime/textMatching/" xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/" targetNamespace="http://www.company.com/Soa/Foundation/" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/">
  <wsdl:types>
    <s:schema elementFormDefault="qualified" targetNamespace="http://www.company.com/Soa/Foundation/">
      <s:import namespace="http://www.company.com/Soa/Foundation/MessageDefinition.xsd" />
      <s:element name="GetSearchableAttributes">
        <s:complexType>
          <s:sequence>
            <s:element minOccurs="0" maxOccurs="1" ref="s1:GetSearchableAttributeRequest" />
          </s:sequence>
        </s:complexType>
      </s:element>
      <s:element name="GetSearchableAttributesResponse">
        <s:complexType>
          <s:sequence>
            <s:element minOccurs="0" maxOccurs="1" ref="s1:GetSearchableAttributeResponse" />
          </s:sequence>
        </s:complexType>
      </s:element>
      <s:element name="RequestHeader" type="tns:RequestHeader" />
      <s:complexType name="RequestHeader">
        <s:sequence>
          <s:element minOccurs="0" maxOccurs="1" name="Headers" type="tns:ArrayOfAnyType" />
        </s:sequence>
        <s:anyAttribute />
      </s:complexType>
      <s:complexType name="ArrayOfAnyType">
        <s:sequence>
          <s:element minOccurs="0" maxOccurs="unbounded" name="anyType" nillable="true" />
        </s:sequence>
      </s:complexType>
      <s:element name="ResponseHeader" type="tns:ResponseHeader" />
      <s:complexType name="ResponseHeader">
        <s:sequence>
          <s:element minOccurs="0" maxOccurs="1" name="Headers" type="tns:ArrayOfAnyType" />
        </s:sequence>
        <s:anyAttribute />
      </s:complexType>
      <s:element name="GetEntity">
        <s:complexType>
          <s:sequence>
            <s:element minOccurs="0" maxOccurs="1" ref="s1:GetEntityRequest" />
          </s:sequence>
        </s:complexType>
      </s:element>
      <s:element name="GetEntityResponse">
        <s:complexType>
          <s:sequence>
            <s:element minOccurs="0" maxOccurs="1" ref="s1:GetEntityResponse" />
          </s:sequence>
        </s:complexType>
      </s:element>
    </s:schema>
    <s:schema elementFormDefault="qualified" targetNamespace="http://www.company.com/Soa/Foundation/MessageDefinition.xsd">
      <s:element name="GetSearchableAttributeRequest" type="s1:GetSearchableAttributeRequest" />
      <s:complexType name="GetSearchableAttributeRequest">
        <s:complexContent mixed="false">
          <s:extension base="s1:GenericRequest">
            <s:sequence>
              <s:element minOccurs="0" maxOccurs="1" name="GetSearchableAttributes" type="s1:ArrayOfGetSearchableAttributeInMsg" />
            </s:sequence>
          </s:extension>
        </s:complexContent>
      </s:complexType>
      <s:complexType name="GenericRequest">
        <s:sequence>
          <s:element minOccurs="0" maxOccurs="1" name="TokenId" type="s:string" />
        </s:sequence>
      </s:complexType>
      <s:complexType name="ArrayOfGetSearchableAttributeInMsg">
        <s:sequence>
          <s:element minOccurs="0" maxOccurs="unbounded" name="GetSearchableAttributeInMsg" nillable="true" type="s1:GetSearchableAttributeInMsg" />
        </s:sequence>
      </s:complexType>
      <s:complexType name="GetSearchableAttributeInMsg">
        <s:complexContent mixed="false">
          <s:extension base="s1:GenericInMsg">
            <s:sequence>
              <s:element minOccurs="0" maxOccurs="1" name="BusinessEntityType" type="s:string" />
            </s:sequence>
          </s:extension>
        </s:complexContent>
      </s:complexType>
      <s:complexType name="GenericInMsg">
        <s:complexContent mixed="false">
          <s:extension base="s1:GenericMsg">
            <s:sequence>
              <s:element minOccurs="0" maxOccurs="1" name="MessageState" type="s:string" />
            </s:sequence>
          </s:extension>
        </s:complexContent>
      </s:complexType>
      <s:complexType name="GenericMsg">
        <s:sequence>
          <s:element minOccurs="0" maxOccurs="1" default="-1" name="MessageId" type="s:int" />
          <s:element minOccurs="0" maxOccurs="1" name="CustomAttributes" type="s1:ArrayOfCustomAttributeMsg" />
          <s:element minOccurs="0" maxOccurs="1" default="-1" name="CorrelationId" type="s:int" />
          <s:element minOccurs="0" maxOccurs="1" default="false" name="SkipSemanticValidation" type="s:boolean" />
        </s:sequence>
      </s:complexType>
      <s:complexType name="ArrayOfCustomAttributeMsg">
        <s:sequence>
          <s:element minOccurs="0" maxOccurs="unbounded" name="CustomAttributeMsg" nillable="true" type="s1:CustomAttributeMsg" />
        </s:sequence>
      </s:complexType>
      <s:complexType name="CustomAttributeMsg">
        <s:sequence>
          <s:element minOccurs="0" maxOccurs="1" name="Name" type="s:string" />
          <s:element minOccurs="0" maxOccurs="1" name="Value" type="s:string" />
        </s:sequence>
      </s:complexType>
      <s:complexType name="GenericOutMsg">
        <s:complexContent mixed="false">
          <s:extension base="s1:GenericMsg">
            <s:sequence>
              <s:element minOccurs="0" maxOccurs="1" name="MessageResult" type="s:string" />
              <s:element minOccurs="0" maxOccurs="1" default="OK" name="MessageStatus" type="s1:MessageStatusType" />
              <s:element minOccurs="0" maxOccurs="1" name="MessageErrorCode" type="s:string" />
            </s:sequence>
          </s:extension>
        </s:complexContent>
      </s:complexType>
      <s:simpleType name="MessageStatusType">
        <s:restriction base="s:string">
          <s:enumeration value="OK" />
          <s:enumeration value="FailedValidation" />
          <s:enumeration value="FailedExecution" />
          <s:enumeration value="FailedAuthorization" />
          <s:enumeration value="FailedOther" />
        </s:restriction>
      </s:simpleType>
      <s:complexType name="GetSearchableAttributeOutMsg">
        <s:complexContent mixed="false">
          <s:extension base="s1:GenericOutMsg">
            <s:sequence>
              <s:element minOccurs="0" maxOccurs="1" name="BusinessEntityType" type="s:string" />
              <s:element minOccurs="0" maxOccurs="unbounded" name="SearchableAttributes" type="s1:CustomAttributeMsg" />
            </s:sequence>
          </s:extension>
        </s:complexContent>
      </s:complexType>
      <s:element name="GetSearchableAttributeResponse" type="s1:GetSearchableAttributeResponse" />
      <s:complexType name="GetSearchableAttributeResponse">
        <s:complexContent mixed="false">
          <s:extension base="s1:GenericResponse">
            <s:sequence>
              <s:element minOccurs="0" maxOccurs="1" name="GetSearchableAttributes" type="s1:ArrayOfGetSearchableAttributeOutMsg" />
            </s:sequence>
          </s:extension>
        </s:complexContent>
      </s:complexType>
      <s:complexType name="GenericResponse">
        <s:sequence>
          <s:element minOccurs="1" maxOccurs="1" name="Status" type="s1:TrxStatus" />
          <s:element minOccurs="0" maxOccurs="1" name="TrxResult" type="s:string" />
          <s:element minOccurs="0" maxOccurs="1" name="TokenId" type="s:string" />
        </s:sequence>
      </s:complexType>
      <s:simpleType name="TrxStatus">
        <s:restriction base="s:string">
          <s:enumeration value="OK" />
          <s:enumeration value="ErrorSQL" />
          <s:enumeration value="ErrorBusinessLogic" />
          <s:enumeration value="ErrorWebService" />
          <s:enumeration value="ErrorArguments" />
          <s:enumeration value="ErrorSecurity" />
          <s:enumeration value="ErrorSystem" />
          <s:enumeration value="ErrorMultiple" />
        </s:restriction>
      </s:simpleType>
      <s:complexType name="ArrayOfGetSearchableAttributeOutMsg">
        <s:sequence>
          <s:element minOccurs="0" maxOccurs="unbounded" name="GetSearchableAttributeOutMsg" nillable="true" type="s1:GetSearchableAttributeOutMsg" />
        </s:sequence>
      </s:complexType>
      <s:element name="GetEntityRequest" type="s1:GetEntityRequest" />
      <s:complexType name="GetEntityRequest">
        <s:complexContent mixed="false">
          <s:extension base="s1:GenericRequest">
            <s:sequence>
              <s:element minOccurs="0" maxOccurs="1" name="Entities" type="s1:ArrayOfGetEntityInMsg" />
            </s:sequence>
          </s:extension>
        </s:complexContent>
      </s:complexType>
      <s:complexType name="ArrayOfGetEntityInMsg">
        <s:sequence>
          <s:element minOccurs="0" maxOccurs="unbounded" name="GetEntityInMsg" nillable="true" type="s1:GetEntityInMsg" />
        </s:sequence>
      </s:complexType>
      <s:complexType name="GetEntityInMsg">
        <s:complexContent mixed="false">
          <s:extension base="s1:GenericInMsg">
            <s:sequence>
              <s:element minOccurs="0" maxOccurs="1" default="0" name="RowCount" type="s:int" />
              <s:element minOccurs="0" maxOccurs="1" name="Entity" type="s1:EntityRequest" />
            </s:sequence>
          </s:extension>
        </s:complexContent>
      </s:complexType>
      <s:complexType name="EntityRequest">
        <s:complexContent mixed="false">
          <s:extension base="s1:EntityMsg">
            <s:sequence>
              <s:element minOccurs="0" maxOccurs="unbounded" name="EntitySearchClauses" type="s1:EntitySearchClause" />
            </s:sequence>
          </s:extension>
        </s:complexContent>
      </s:complexType>
      <s:complexType name="EntityMsg">
        <s:sequence>
          <s:element minOccurs="1" maxOccurs="1" name="RequestId" type="s:int" />
          <s:element minOccurs="0" maxOccurs="1" name="EntityType" type="s:string" />
          <s:element minOccurs="0" maxOccurs="1" name="EntityAssembly" type="s:string" />
        </s:sequence>
      </s:complexType>
      <s:complexType name="SerializedEntity">
        <s:complexContent mixed="false">
          <s:extension base="s1:EntityMsg">
            <s:sequence>
              <s:element minOccurs="0" maxOccurs="1" name="EntityString" type="s:string" />
            </s:sequence>
          </s:extension>
        </s:complexContent>
      </s:complexType>
      <s:complexType name="EntitySearchClause">
        <s:sequence>
          <s:element minOccurs="0" maxOccurs="unbounded" name="SearchParameters" type="s1:EntitySearchParameter" />
          <s:element minOccurs="1" maxOccurs="1" name="Operator" type="s1:ClauseOperatorType" />
          <s:element minOccurs="0" maxOccurs="unbounded" name="QueryFlags" type="s1:QueryFlagType" />
        </s:sequence>
      </s:complexType>
      <s:complexType name="EntitySearchParameter">
        <s:sequence>
          <s:element minOccurs="0" maxOccurs="1" name="FieldName" type="s:string" />
          <s:element minOccurs="0" maxOccurs="1" name="FieldValue" type="s:string" />
          <s:element minOccurs="1" maxOccurs="1" name="Operator" type="s1:ParameterOperatorType" />
        </s:sequence>
      </s:complexType>
      <s:simpleType name="ParameterOperatorType">
        <s:restriction base="s:string">
          <s:enumeration value="Equal" />
          <s:enumeration value="NotEqual" />
          <s:enumeration value="GreaterThan" />
          <s:enumeration value="GreaterThanOrEqual" />
          <s:enumeration value="LessThan" />
          <s:enumeration value="LessThanOrEqual" />
          <s:enumeration value="Like" />
          <s:enumeration value="IsNull" />
          <s:enumeration value="IsNullOrZero" />
          <s:enumeration value="IsNullOrEmptyString" />
          <s:enumeration value="IsNotNull" />
          <s:enumeration value="Between" />
          <s:enumeration value="In" />
          <s:enumeration value="NotIn" />
          <s:enumeration value="NotLike" />
          <s:enumeration value="BitWiseOR" />
          <s:enumeration value="BitWiseAND" />
          <s:enumeration value="NotBitWiseOR" />
          <s:enumeration value="NotBitWiseAND" />
        </s:restriction>
      </s:simpleType>
      <s:simpleType name="ClauseOperatorType">
        <s:restriction base="s:string">
          <s:enumeration value="And" />
          <s:enumeration value="Or" />
          <s:enumeration value="Undefined" />
        </s:restriction>
      </s:simpleType>
      <s:simpleType name="QueryFlagType">
        <s:restriction base="s:string">
          <s:enumeration value="CountAll" />
          <s:enumeration value="Distinct" />
        </s:restriction>
      </s:simpleType>
      <s:complexType name="ArrayOfGetEntityOutMsg">
        <s:sequence>
          <s:element minOccurs="0" maxOccurs="unbounded" name="GetEntityOutMsg" nillable="true" type="s1:GetEntityOutMsg" />
        </s:sequence>
      </s:complexType>
      <s:complexType name="GetEntityOutMsg">
        <s:complexContent mixed="false">
          <s:extension base="s1:GenericOutMsg">
            <s:sequence>
              <s:element minOccurs="0" maxOccurs="unbounded" name="SerializedEntities" type="s1:SerializedEntity" />
            </s:sequence>
          </s:extension>
        </s:complexContent>
      </s:complexType>
      <s:element name="GetEntityResponse" type="s1:GetEntityResponse" />
      <s:complexType name="GetEntityResponse">
        <s:complexContent mixed="false">
          <s:extension base="s1:GenericResponse">
            <s:sequence>
              <s:element minOccurs="0" maxOccurs="1" name="EntityList" type="s1:ArrayOfGetEntityOutMsg" />
            </s:sequence>
          </s:extension>
        </s:complexContent>
      </s:complexType>
    </s:schema>
  </wsdl:types>
  <wsdl:message name="GetSearchableAttributesSoapIn">
    <wsdl:part name="parameters" element="tns:GetSearchableAttributes" />
  </wsdl:message>
  <wsdl:message name="GetSearchableAttributesSoapOut">
    <wsdl:part name="parameters" element="tns:GetSearchableAttributesResponse" />
  </wsdl:message>
  <wsdl:message name="GetSearchableAttributesRequestHeader">
    <wsdl:part name="RequestHeader" element="tns:RequestHeader" />
  </wsdl:message>
  <wsdl:message name="GetSearchableAttributesResponseHeader">
    <wsdl:part name="ResponseHeader" element="tns:ResponseHeader" />
  </wsdl:message>
  <wsdl:message name="GetEntitySoapIn">
    <wsdl:part name="parameters" element="tns:GetEntity" />
  </wsdl:message>
  <wsdl:message name="GetEntitySoapOut">
    <wsdl:part name="parameters" element="tns:GetEntityResponse" />
  </wsdl:message>
  <wsdl:message name="GetEntityRequestHeader">
    <wsdl:part name="RequestHeader" element="tns:RequestHeader" />
  </wsdl:message>
  <wsdl:message name="GetEntityResponseHeader">
    <wsdl:part name="ResponseHeader" element="tns:ResponseHeader" />
  </wsdl:message>
  <wsdl:portType name="GetEntityWebServiceSoap">
    <wsdl:operation name="GetSearchableAttributes">
      <wsdl:input message="tns:GetSearchableAttributesSoapIn" />
      <wsdl:output message="tns:GetSearchableAttributesSoapOut" />
    </wsdl:operation>
    <wsdl:operation name="GetEntity">
      <wsdl:input message="tns:GetEntitySoapIn" />
      <wsdl:output message="tns:GetEntitySoapOut" />
    </wsdl:operation>
  </wsdl:portType>
  <wsdl:binding name="GetEntityWebServiceSoap" type="tns:GetEntityWebServiceSoap">
    <soap:binding transport="http://schemas.xmlsoap.org/soap/http" />
    <wsdl:operation name="GetSearchableAttributes">
      <soap:operation soapAction="http://www.company.com/Soa/Foundation/GetSearchableAttributes" style="document" />
      <wsdl:input>
        <soap:body use="literal" />
        <soap:header message="tns:GetSearchableAttributesRequestHeader" part="RequestHeader" use="literal" />
      </wsdl:input>
      <wsdl:output>
        <soap:body use="literal" />
        <soap:header message="tns:GetSearchableAttributesResponseHeader" part="ResponseHeader" use="literal" />
      </wsdl:output>
    </wsdl:operation>
    <wsdl:operation name="GetEntity">
      <soap:operation soapAction="http://www.company.com/Soa/Foundation/GetEntity" style="document" />
      <wsdl:input>
        <soap:body use="literal" />
        <soap:header message="tns:GetEntityRequestHeader" part="RequestHeader" use="literal" />
      </wsdl:input>
      <wsdl:output>
        <soap:body use="literal" />
        <soap:header message="tns:GetEntityResponseHeader" part="ResponseHeader" use="literal" />
      </wsdl:output>
    </wsdl:operation>
  </wsdl:binding>
  <wsdl:binding name="GetEntityWebServiceSoap12" type="tns:GetEntityWebServiceSoap">
    <soap12:binding transport="http://schemas.xmlsoap.org/soap/http" />
    <wsdl:operation name="GetSearchableAttributes">
      <soap12:operation soapAction="http://www.company.com/Soa/Foundation/GetSearchableAttributes" style="document" />
      <wsdl:input>
        <soap12:body use="literal" />
        <soap12:header message="tns:GetSearchableAttributesRequestHeader" part="RequestHeader" use="literal" />
      </wsdl:input>
      <wsdl:output>
        <soap12:body use="literal" />
        <soap12:header message="tns:GetSearchableAttributesResponseHeader" part="ResponseHeader" use="literal" />
      </wsdl:output>
    </wsdl:operation>
    <wsdl:operation name="GetEntity">
      <soap12:operation soapAction="http://www.company.com/Soa/Foundation/GetEntity" style="document" />
      <wsdl:input>
        <soap12:body use="literal" />
        <soap12:header message="tns:GetEntityRequestHeader" part="RequestHeader" use="literal" />
      </wsdl:input>
      <wsdl:output>
        <soap12:body use="literal" />
        <soap12:header message="tns:GetEntityResponseHeader" part="ResponseHeader" use="literal" />
      </wsdl:output>
    </wsdl:operation>
  </wsdl:binding>
  <wsdl:service name="GetEntityWebService">
    <wsdl:port name="GetEntityWebServiceSoap" binding="tns:GetEntityWebServiceSoap">
      <soap:address location="http://10.0.1.5/WebServices/GetEntityWebService.asmx" />
    </wsdl:port>
    <wsdl:port name="GetEntityWebServiceSoap12" binding="tns:GetEntityWebServiceSoap12">
      <soap12:address location="http://10.0.1.5/WebServices/GetEntityWebService.asmx" />
    </wsdl:port>
  </wsdl:service>
</wsdl:definitions>
requirefeedback

Most helpful comment

Ok, turned out I had to do

result = client.service.method(
            Query={
                'Criteria': {
                    'Criterion': {
                      'Ref': [{code: '000'}, {code: '001'}]
                    }
                }
            })

And it works... so ignore me.

All 17 comments

Do you have the xsd for me? I cant reproduce it without it.

Hi @mvantellingen, I believe I might've run into this as well. I have an interface that has the following section in its WSDL:

      <s:complexType name="ArrayOfString">
        <s:sequence>
          <s:element minOccurs="0" maxOccurs="unbounded" name="string" nillable="true" type="s:string" />
        </s:sequence>
      </s:complexType>

      <s:element name="GetX">
        <s:complexType>
          <s:sequence>
            <s:element minOccurs="0" maxOccurs="1" name="sn" type="s:string" />
            <s:element minOccurs="0" maxOccurs="1" name="names" type="tns:ArrayOfString" />
          </s:sequence>
        </s:complexType>
      </s:element>

I then try to make use of this in the following way:

def string(value):
    return {
        'string': value
    }

_names = ['itemName1', 'itemName2', 'itemName3', 'itemName3.subItem1', 'itemName3.subItem2']
_p = [string(x) for x in _names]
_sn = 1234567890

resp = client.service.GetX(sn=_sn, names=_p)

This results in an empty names section in my request (<ns0:names/> is shown in the debug output).

Whenever I reduce _names to a single-entry list, I get a correct request (and a server response).

My XML document header is <?xml version="1.0" encoding="utf-8"?><soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">, and the WSDL is correctly in the namespace of the SOAP request body.

(Please let me know if you require additional info on this)

I've just created a branch which should properly handle wsdl:arrayTypes (branch is better-arrays) could you give that a try? PR is #181

Hi! Thanks for the branch, I'll certainly test it out for you.

No feedback is good feedback ? :D

Haven't had an opportunity to test it out yet. I went another direction after posting this issue - using HTTP and parsing xml document myself (painful). When I get the chance, I'll test it out. Thanks!

Ok - I tested it out. What I found is that it still doesn't work. The error has gone away - but the elements of the array are not being serialized and sent to the server ... only the name of the array is sent, which results in an empty array when the server de-serializes the message. I'll try to get a good example to post for your review.

I'm having similar problem as bmiller, elements of array are not serialized and sent, service gets only an empty array. For test I wrote following method on ASP.NET service:

[WebMethod]
public int ArrayLength(String[] param)
{
    return param.Length;
}

It has following WSDL:

<s:element name="ArrayLength">
    <s:complexType>
        <s:sequence>
            <s:element minOccurs="0" maxOccurs="1" name="param" type="tns:ArrayOfString"/>
        </s:sequence>
    </s:complexType>
</s:element>
<s:complexType name="ArrayOfString">
    <s:sequence>
        <s:element minOccurs="0" maxOccurs="unbounded" name="string" nillable="true" type="s:string"/>
    </s:sequence>
</s:complexType>

And if we try to call it with following python-zeep code, result should be two, but it is zero.

URL = 'http://192.168.56.103/zeepbug/ZeepBug.asmx?wsdl'
client = zeep.Client(URL)
result = client.service.ArrayLength(['one', 'two'])
print result

However, there is a workaround, if we call it with following code, result will be two:

URL = 'http://192.168.56.103/zeepbug/ZeepBug.asmx?wsdl'
client = zeep.Client(URL)
factory = client.type_factory('ns0')
result = client.service.ArrayLength(factory.ArrayOfString(['one', 'two']))
print result

I tested pip-installed release 0.22.0 and current master branch (commit ec6216).

@mvantellingen Got the same problem - maybe you can reopen this issue?

WSDL (partly):

  <wsdl:message name="save_master_data">
    <wsdl:part name="atk" type="xsd:string">
    </wsdl:part>
    <wsdl:part name="type" type="xsd:string">
    </wsdl:part>
    <wsdl:part name="data" type="tns:masterObjectDataArray">
    </wsdl:part>
  </wsdl:message>

  <xs:complexType final="#all" name="masterObjectDataArray">
    <xs:sequence>
      <xs:element maxOccurs="unbounded" minOccurs="0" name="item" nillable="true" type="tns:masterObjectData"/>
    </xs:sequence>
  </xs:complexType>

  <xs:complexType name="masterObjectData">
    <xs:sequence>
      <xs:element name="id" type="xs:long"/>
      <xs:element minOccurs="0" name="name" type="xs:string"/>
      <xs:element name="alive" type="xs:boolean"/>
      <xs:element minOccurs="0" name="custom" type="xs:string"/>
    </xs:sequence>
  </xs:complexType>

Method called:

objects = [
 client.type_factory('ns0').masterObjectData(id=1, name="Object1", alive=true, custom=""),
 client.type_factory('ns0').masterObjectData(id=2, name="Object2", alive=true, custom=""),
 client.type_factory('ns0').masterObjectData(id=3, name="Object3", alive=true, custom="")
]
client.save_master_data("foo", "bar", objects)

Request beeing sent:

<soap-env:Envelope xmlns:soap-env="http://www.w3.org/2003/05/soap-envelope">
  <soap-env:Body>
    <ns0:save_master_data xmlns:ns0="http://example">
      <atk>foo</atk>
      <type>bar</type>
      <data/>
    </ns0:save_master_data>
  </soap-env:Body>
</soap-env:Envelope>

Python Debug (generation of objects seems to be successfull):

print(repr(objects))
#==>
[{
    'id': 1,
    'name': 'Object1',
    'alive': True,
    'custom': "" },
{
    'id': 2,
    'name': 'Object2',
    'alive': True,
    'custom': "" },
[...] ]

Expected (afaik):

<soap-env:Envelope xmlns:soap-env="http://www.w3.org/2003/05/soap-envelope">
  <soap-env:Body>
    <ns0:save_master_data xmlns:ns0="http://example">
      <atk>foo</atk>
      <type>bar</type>
      <data>
       <masterObjectData>
         <id>1</id>
         <name>Object1</name>
         <custom></custom>
       </masterObjectData>
        <masterObjectData>
          <id>2</id>
          <name>Object2</name>
          <custom></custom>
        </masterObjectData>
[...]
      </data>
    </ns0:save_master_data>
  </soap-env:Body>
</soap-env:Envelope>

Same here. xsd has

<xs:element name="Criterion" minOccurs="0">
  <xs:complexType>
     <xs:sequence>
       <xs:element name="Ref" minOccurs="0" maxOccurs="unbounded">...</xs:element>
     </xs:sequence>
  </xs:complexType>
</xs:element>

But when I pass in an array I end up with actually only 1 item passed through.

        result = client.service.method(
            Query={
                'Criteria': {
                    'Criterion': [{Ref: {code: '000'}}, {Ref: {code: '001'}}]
                }
            })

Logging the SOAP request logs

          <ns0:Criterion>
            <ns0:Ref code="000"/>
          </ns0:Criterion>

Ok, turned out I had to do

result = client.service.method(
            Query={
                'Criteria': {
                    'Criterion': {
                      'Ref': [{code: '000'}, {code: '001'}]
                    }
                }
            })

And it works... so ignore me.

Just way to add that the answer provided by @jhorman of "turned out I had to do" saved me, so it shouldn't be ignored. Anyone comes here for an answer, that was it. Structuring array like that to get the result I wanted/expected, was not intuitive at all, so thanks for posting it.

In @jhorman's solution, is "code" a keyword? Zeep gives me "'code' is not defined". And while I see 'Criterion' and 'Ref' in his XSD, I'm not seeing where 'Criteria' came from. I assume "method" should be replaced by the method I am calling, but isn't that "Criterion" in this example? And I assume "Query" is replaced by the name of the argument that I am passing to the web service, but I am confused as to why it's not "Ref" in that example? Any help would be much appreciated as I'm having the same issues passing a simple array. ....Larry

Figured it out. For my XSD:
my XSD:






I had to use:
returnString = client.service.GetRoomStatusColor(Room={"string": ["Room1", "Room2"]}, Now = 12345)

Hope this is helpful to other newbies like me. And @mvantellingen, thanks for Zeep!

Super helpful. I struggled with this for hours. Thank you @CompuDoc00!! That post shows the correct way to format arrays in zeep. Thanks also to @mvantellingen for this great library.

to recap, you gotta use myobject_dict = serialize_object(my_object, dict) from the zeep.helpers to convert your object to a dict, and then do something like myobject_dict['list_of_values'] = {"Type_name_of_values": myobject_dict['list_of_values']}

Maybe someone can find out why it's not converted into a etree elements list when you pass your zeep element output.

anyway, i thank you all very much, this hack saved my day

Was this page helpful?
0 / 5 - 0 ratings

Related issues

pope1ni picture pope1ni  路  4Comments

nihankarslioglu picture nihankarslioglu  路  4Comments

grguthrie picture grguthrie  路  4Comments

olivierverville picture olivierverville  路  3Comments

orzilca picture orzilca  路  6Comments