mvantellingen / python-zeep

A Python SOAP client
http://docs.python-zeep.org
Other
1.88k stars 586 forks source link

An error occurred while parsing an response containing a sequence with a choice. #1263

Open random343 opened 3 years ago

random343 commented 3 years ago

There is a data schema describing the response from the service test.xsd.txt test.xsd.txt

When parsing a response from the service, zeep returns an error: "Unexpected element '.

The error is reproduced on zeep master commit 4e16b7726b5027e83d0dbe911bd45f153b7a09dc

For example: response

        <?xml version="1.0" encoding="utf-8"?>
        <soap:Envelope
            xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"
            xmlns:xsd="http://www.w3.org/2001/XMLSchema"
            xmlns:tns="urn:test">
          <soap:Body>
              <tns:TestResponse>
                <tns:sessionId>SESSION_ID</tns:sessionId>
                <tns:code>CODE_TEXT</tns:code>
                <tns:name>NAME_TEXT</tns:name>
              </tns:TestResponse>
          </soap:Body>
        </soap:Envelope>

parses correctly;

And parsing the response

        <?xml version="1.0" encoding="utf-8"?>
        <soap:Envelope
            xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"
            xmlns:xsd="http://www.w3.org/2001/XMLSchema"
            xmlns:tns="urn:test">
          <soap:Body>
              <tns:TestResponse>
                <tns:sessionId>SESSION_ID</tns:sessionId>
                <tns:error>ERROR_TEXT</tns:error>
              </tns:TestResponse>
          </soap:Body>
        </soap:Envelope>

returns an error: "Unexpected element '{urn:test}error', expected 'code'".

Here is my code: test.py.txt

# -*- coding: utf-8 -*-

from lxml import etree
from zeep.xsd import Schema

def parse_response(response):
    nsmap = {
        'soap': 'http://schemas.xmlsoap.org/soap/envelope/',
        'xsd': 'http://www.w3.org/2001/XMLSchema',
        'tns': 'urn:test',
    }
    schema_node = etree.fromstring(b"""
        <?xml version="1.0"?>
        <schema targetNamespace="urn:test"
                xmlns="http://www.w3.org/2001/XMLSchema"
                xmlns:xsd="http://www.w3.org/2001/XMLSchema"
                xmlns:tns="urn:test">
            <complexType name="TestResponseType">
                <sequence>
                    <element name="sessionId" type="xsd:string"></element>
                    <choice>
                        <sequence>
                            <element name="code" type="xsd:string"></element>
                            <element name="name" type="xsd:string"></element>
                        </sequence>
                        <element name="error" type="xsd:string"></element>
                    </choice>
                </sequence>
            </complexType>
            <element name="TestResponse" type="tns:TestResponseType">
            </element>
        </schema>
    """.strip())
    schema = Schema(schema_node)
    element_name = 'TestResponse'
    element = schema.get_element('{' + nsmap.get('tns') + '}' + element_name)
    response_node = etree.fromstring(response.strip())
    node = response_node.find('soap:Body/tns:' + element_name, namespaces=nsmap)
    obj = element.parse(node, schema)
    return obj

if __name__ == '__main__':
    response = b"""
        <?xml version="1.0" encoding="utf-8"?>
        <soap:Envelope
            xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"
            xmlns:xsd="http://www.w3.org/2001/XMLSchema"
            xmlns:tns="urn:test">
          <soap:Body>
              <tns:TestResponse>
                <tns:sessionId>SESSION_ID</tns:sessionId>
                <tns:code>CODE_TEXT</tns:code>
                <tns:name>NAME_TEXT</tns:name>
              </tns:TestResponse>
          </soap:Body>
        </soap:Envelope>
    """
    try:
        print('Parse response...')
        obj = parse_response(response)
        assert obj.sessionId == 'SESSION_ID'
        assert obj.code == 'CODE_TEXT'
        assert obj.name == 'NAME_TEXT'
    except Exception as e:
        print(e)
    else:
        print('OK!')

    response = b"""
        <?xml version="1.0" encoding="utf-8"?>
        <soap:Envelope
            xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"
            xmlns:xsd="http://www.w3.org/2001/XMLSchema"
            xmlns:tns="urn:test">
          <soap:Body>
              <tns:TestResponse>
                <tns:sessionId>SESSION_ID</tns:sessionId>
                <tns:error>ERROR_TEXT</tns:error>
              </tns:TestResponse>
          </soap:Body>
        </soap:Envelope>
    """
    try:
        print('Parse response...')
        obj = parse_response(response)
        assert obj.sessionId == 'SESSION_ID'
        assert obj.error == 'ERROR_TEXT'
    except Exception as e:
        print(e)
    else:
        print('OK!')

It seems to me that the reason is that, in the zeep.xsd.elements.indicators module in the Sequence class in the parse_xmlelements method, the elements should be traversed not by self.elements, but by self.elements_nested. Then, my code runs without error.