[tryton-debian-vcs] python-zeep branch debian updated. debian/0.23.0-1-5-gcad63e6

Mathias Behrle tryton-debian-vcs at alioth.debian.org
Tue Jan 31 16:44:23 UTC 2017


The following commit has been merged in the debian branch:
https://alioth.debian.org/plugins/scmgit/cgi-bin/gitweb.cgi/?p=tryton/python-zeep.git;a=commitdiff;h=debian/0.23.0-1-5-gcad63e6
  discards  c25d68aac10d5bf8ab4f9ddee428482c06d55b17 (commit)
  discards  76a9dacf0b9241ab02265039ecc32669a027998e (commit)
  discards  d20635c0d4a826ef35c1821cab81024b52f09371 (commit)

This update added new revisions after undoing existing revisions.  That is
to say, the old revision is not a strict subset of the new revision.  This
situation occurs when you --force push a change and generate a repository
containing something like this:

 * -- * -- B -- O -- O -- O (c25d68aac10d5bf8ab4f9ddee428482c06d55b17)
            \
             N -- N -- N (cad63e607ff6c399f313c851ef64de726e35e09f)

When this happens we assume that you've already had alert emails for all
of the O revisions, and so we here report only the revisions in the N
branch from the common base, B.

commit cad63e607ff6c399f313c851ef64de726e35e09f
Author: Mathias Behrle <mathiasb at m9s.biz>
Date:   Tue Jan 31 17:44:19 2017 +0100

    Updating Build-Depends and Depends for 0.27.0.

diff --git a/debian/control b/debian/control
index 99cbd52..875a481 100644
--- a/debian/control
+++ b/debian/control
@@ -7,6 +7,8 @@ Uploaders:
 Build-Depends:
  debhelper (>= 9),
  dh-python (>= 1.20130901-1~),
+ libxmlsec1,
+ libxmlsec1-openssl,
  python,
  python-appdirs,
  python-cached-property,
@@ -22,6 +24,7 @@ Build-Depends:
  python-pytest-cov,
  python-requests,
  python-requests-mock,
+ python-requests-toolbelt,
  python-setuptools,
  python-six (>= 1.9.0-3~),
  python-tz,
@@ -59,6 +62,7 @@ Depends:
  python-lxml,
  python-pkg-resources,
  python-requests,
+ python-requests-toolbelt,
  python-setuptools,
  python-six (>= 1.9.0-3~),
  python-tz,
commit c5291c55e4d416a4afed9f0f76ff8ffc62aa65bf
Author: Mathias Behrle <mathiasb at m9s.biz>
Date:   Tue Jan 31 16:59:58 2017 +0100

    Merging upstream version 0.27.0.

diff --git a/CHANGES b/CHANGES
index 1b6c737..95e55c4 100644
--- a/CHANGES
+++ b/CHANGES
@@ -1,3 +1,39 @@
+0.27.0 (2017-01-28)
+-------------------
+ - Add support for SOAP attachments (multipart responses). (Dave Wapstra, #302)
+ - Update xsd:anyType to return the xml elements when no type is given via the
+   xsi:type attribute (#284)
+ - Fix parsing Any elements when a restriction is used (soap-enc:array) (#322)
+
+
+0.26.0 (2017-01-26)
+-------------------
+This release again introduces some backwords incompatibilties. The next release
+will hopefully be 1.0 which will introduce semver.
+
+ - **backwards-incompatible**: The Transport class now accepts a 
+   ``requests.Session()`` object instead of ``http_auth`` and ``verify``. This
+   allows for more flexibility.
+ - **backwards-incompatible**: Zeep no longer sets a default cache backend.
+   Please see http://docs.python-zeep.org/en/master/transport.html#caching for
+   information about how to configure a cache.
+ - Add ``zeep.xsd.SkipValue`` which instructs the serialize to ignore the 
+   element.
+ - Support duplicate target namespaces in the wsdl definition (#320)
+ - Fix resolving element/types for xsd schema's with duplicate tns (#319)
+
+
+0.25.0 (2017-01-23)
+-------------------
+ - **Important:** Add basic validation against the xsd. It currently will only
+   validate the minOccurs/maxOccurs but this will be extended in the future.
+ - Add support for duplicate namespace definitions. Previously imports for
+   namespaces which were already imported were ignored. It will now search
+   through all matching schemas with the tns to find a specific object (#204)
+ - Fix xsd:extension for sequence -> choice. (#257)
+ - Improve serializing attributes when the values were passed as a dict (#125)
+
+
 0.24.0 (2016-12-16)
 -------------------
  - Don't fail the parsing of responses if an xsi:type references an non-existing
diff --git a/LICENSE b/LICENSE
index 9310f1f..75eecda 100644
--- a/LICENSE
+++ b/LICENSE
@@ -21,6 +21,8 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 SOFTWARE.
 
 
+--
+
 Parts of the XSD handling are heavily inspired by soapfish, see:
 https://github.com/FlightDataServices/soapfish
 
@@ -51,3 +53,40 @@ INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
 IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
 OF SUCH DAMAGE.
+
+
+--
+
+The support for BinarySecurityToken is from py-wsse, see:
+https://github.com/orcasgit/py-wsse
+
+
+Copyright (c) 2015 ORCAS, Inc
+Some portions from py-soap-wsse (c) Michael van Tellingen
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+    * Redistributions of source code must retain the above copyright
+      notice, this list of conditions and the following disclaimer.
+    * Redistributions in binary form must reproduce the above
+      copyright notice, this list of conditions and the following
+      disclaimer in the documentation and/or other materials provided
+      with the distribution.
+    * Neither the name of the author nor the names of other
+      contributors may be used to endorse or promote products derived
+      from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/PKG-INFO b/PKG-INFO
index fc65fe8..0dcc777 100644
--- a/PKG-INFO
+++ b/PKG-INFO
@@ -1,6 +1,6 @@
 Metadata-Version: 1.1
 Name: zeep
-Version: 0.24.0
+Version: 0.27.0
 Summary: A modern/fast Python SOAP client based on lxml / requests
 Home-page: http://docs.python-zeep.org
 Author: Michael van Tellingen
@@ -12,23 +12,14 @@ Description: ========================
         
         A fast and modern Python SOAP client
         
-        | Website: http://docs.python-zeep.org/
-        | IRC: #python-zeep on Freenode
-        
         Highlights:
-         * Modern codebase compatible with Python 2.7, 3.3, 3.4, 3.5 and PyPy
+         * Compatible with Python 2.7, 3.3, 3.4, 3.5, 3.6 and PyPy
          * Build on top of lxml and requests
-         * Supports recursive WSDL and XSD documents.
-         * Supports the xsd:choice and xsd:any elements.
-         * Uses the defusedxml module for handling potential XML security issues
-         * Support for WSSE (UsernameToken only for now)
-         * Experimental support for HTTP bindings
-         * Experimental support for WS-Addressing headers
+         * Support for Soap 1.1, Soap 1.2 and HTTP bindings
+         * Support for WS-Addressing headers
+         * Support for WSSE (UserNameToken / x.509 signing)
          * Experimental support for asyncio via aiohttp (Python 3.5+)
         
-        Features still in development include:
-         * WSSE x.509 support (BinarySecurityToken)
-         * WS Policy support
         
         Please see for more information the documentation at
         http://docs.python-zeep.org/
@@ -56,7 +47,7 @@ Description: ========================
         
         To quickly inspect a WSDL file use::
         
-            python -mzeep <url-to-wsdl>
+            python -m zeep <url-to-wsdl>
         
         
         Please see the documentation at http://docs.python-zeep.org for more
@@ -85,5 +76,6 @@ Classifier: Programming Language :: Python :: 3
 Classifier: Programming Language :: Python :: 3.3
 Classifier: Programming Language :: Python :: 3.4
 Classifier: Programming Language :: Python :: 3.5
+Classifier: Programming Language :: Python :: 3.6
 Classifier: Programming Language :: Python :: Implementation :: CPython
 Classifier: Programming Language :: Python :: Implementation :: PyPy
diff --git a/README.rst b/README.rst
index e46a8b6..c71c9c6 100644
--- a/README.rst
+++ b/README.rst
@@ -4,23 +4,14 @@ Zeep: Python SOAP client
 
 A fast and modern Python SOAP client
 
-| Website: http://docs.python-zeep.org/
-| IRC: #python-zeep on Freenode
-
 Highlights:
- * Modern codebase compatible with Python 2.7, 3.3, 3.4, 3.5 and PyPy
+ * Compatible with Python 2.7, 3.3, 3.4, 3.5, 3.6 and PyPy
  * Build on top of lxml and requests
- * Supports recursive WSDL and XSD documents.
- * Supports the xsd:choice and xsd:any elements.
- * Uses the defusedxml module for handling potential XML security issues
- * Support for WSSE (UsernameToken only for now)
- * Experimental support for HTTP bindings
- * Experimental support for WS-Addressing headers
+ * Support for Soap 1.1, Soap 1.2 and HTTP bindings
+ * Support for WS-Addressing headers
+ * Support for WSSE (UserNameToken / x.509 signing)
  * Experimental support for asyncio via aiohttp (Python 3.5+)
 
-Features still in development include:
- * WSSE x.509 support (BinarySecurityToken)
- * WS Policy support
 
 Please see for more information the documentation at
 http://docs.python-zeep.org/
@@ -71,7 +62,7 @@ Usage
 
 To quickly inspect a WSDL file use::
 
-    python -mzeep <url-to-wsdl>
+    python -m zeep <url-to-wsdl>
 
 
 Please see the documentation at http://docs.python-zeep.org for more
diff --git a/examples/async_client.py b/examples/async_client.py
new file mode 100644
index 0000000..4ade1d9
--- /dev/null
+++ b/examples/async_client.py
@@ -0,0 +1,62 @@
+import asyncio
+import time
+
+import zeep
+
+from zeep.asyncio import AsyncTransport
+
+
+def run_async():
+    print("async example")
+    print("=============")
+
+    result = []
+
+    def handle_future(future):
+        result.extend(future.result())
+
+    loop = asyncio.get_event_loop()
+
+    transport = AsyncTransport(loop, cache=None)
+    client = zeep.Client('http://localhost:8000/?wsdl', transport=transport)
+
+    tasks = [
+        client.service.slow_request('request-1'),  # takes 1 sec
+        client.service.slow_request('request-2'),  # takes 1 sec
+    ]
+    future = asyncio.gather(*tasks, return_exceptions=True)
+
+    result = []
+    future.add_done_callback(handle_future)
+
+    st = time.time()
+    loop.run_until_complete(future)
+    loop.run_until_complete(transport.session.close())
+    print("time: %.2f" % (time.time() - st))
+    print("result: %s", result)
+    print("")
+    return result
+
+
+def run_sync():
+    print("sync example")
+    print("============")
+    transport = zeep.Transport(cache=None)
+    client = zeep.Client('http://localhost:8000/?wsdl', transport=transport)
+
+    st = time.time()
+    result = [
+        client.service.slow_request('request-1'),  # takes 1 sec
+        client.service.slow_request('request-2'),  # takes 1 sec
+    ]
+    print("Time: %.2f" % (time.time() - st))
+    print("result: %s", result)
+    print("\n")
+
+    return result
+
+
+if __name__ == '__main__':
+    print("")
+    run_async()
+    run_sync()
diff --git a/examples/code39.py b/examples/code39.py
new file mode 100644
index 0000000..eda664f
--- /dev/null
+++ b/examples/code39.py
@@ -0,0 +1,8 @@
+from __future__ import print_function
+import zeep
+
+
+client = zeep.Client(
+    wsdl='http://www.webservicex.net/barcode.asmx?WSDL')
+response = client.service.Code39('1234', 20, ShowCodeString=True, Title='ZEEP')
+print(repr(response))
diff --git a/examples/echo_services.py b/examples/echo_services.py
new file mode 100644
index 0000000..5270f95
--- /dev/null
+++ b/examples/echo_services.py
@@ -0,0 +1,5 @@
+from zeep.client import Client
+
+# RPC style soap service
+client = Client('http://www.soapclient.com/xml/soapresponder.wsdl')
+print(client.service.Method1('zeep', 'soap'))
diff --git a/examples/eu_vat_service.py b/examples/eu_vat_service.py
new file mode 100644
index 0000000..e5ba15e
--- /dev/null
+++ b/examples/eu_vat_service.py
@@ -0,0 +1,7 @@
+from __future__ import print_function
+import zeep
+
+
+client = zeep.Client(
+    wsdl='http://ec.europa.eu/taxation_customs/vies/checkVatService.wsdl')
+print(client.service.checkVat('NL', '170944128B01'))
diff --git a/examples/http_basic_auth.py b/examples/http_basic_auth.py
new file mode 100644
index 0000000..e48f6b5
--- /dev/null
+++ b/examples/http_basic_auth.py
@@ -0,0 +1,14 @@
+from __future__ import print_function
+import zeep
+from zeep.transports import Transport
+
+# Example using basic authentication with a webservice
+
+transport_with_basic_auth = Transport(http_auth=('username', 'password'))
+
+client = zeep.Client(
+    wsdl='http://nonexistent?WSDL',
+    transport=transport_with_basic_auth
+)
+
+client.wsdl.dump()
diff --git a/examples/km_to_miles.py b/examples/km_to_miles.py
new file mode 100644
index 0000000..d8821df
--- /dev/null
+++ b/examples/km_to_miles.py
@@ -0,0 +1,16 @@
+from __future__ import print_function
+import zeep
+
+
+client = zeep.Client(
+    wsdl='http://www.webservicex.net/ConvertSpeed.asmx?WSDL')
+
+client.wsdl.dump()
+
+print (client.service.ConvertSpeed(100, 'kilometersPerhour', 'milesPerhour'))
+
+http_get = client.bind('ConvertSpeeds', 'ConvertSpeedsHttpGet')
+http_post = client.bind('ConvertSpeeds', 'ConvertSpeedsHttpPost')
+
+print(http_get.ConvertSpeed(100, 'kilometersPerhour', 'milesPerhour'))
+print(http_post.ConvertSpeed(100, 'kilometersPerhour', 'milesPerhour'))
diff --git a/examples/soap_server.py b/examples/soap_server.py
new file mode 100644
index 0000000..7ffdd51
--- /dev/null
+++ b/examples/soap_server.py
@@ -0,0 +1,46 @@
+"""
+Example soap server using spyne.
+
+Run with
+
+   uwsgi --http :8000 \
+         --wsgi-file soap_server.py \
+         --virtualenv ~/.pyenv/versions/3.5.2/envs/zeep \
+         -p 10
+
+"""
+import time
+
+from spyne import Application, ServiceBase, Unicode, rpc
+from spyne.protocol.soap import Soap11
+from spyne.server.wsgi import WsgiApplication
+
+
+class ExampleService(ServiceBase):
+
+    @rpc(Unicode, _returns=Unicode)
+    def slow_request(ctx, request_id):
+        time.sleep(1)
+        return u'Request: %s' % request_id
+
+application = Application(
+    services=[ExampleService],
+    tns='http://tests.python-zeep.org/',
+    in_protocol=Soap11(validator='lxml'),
+    out_protocol=Soap11())
+
+application = WsgiApplication(application)
+
+if __name__ == '__main__':
+    import logging
+
+    from wsgiref.simple_server import make_server
+
+    logging.basicConfig(level=logging.DEBUG)
+    logging.getLogger('spyne.protocol.xml').setLevel(logging.DEBUG)
+
+    logging.info("listening to http://127.0.0.1:8000")
+    logging.info("wsdl is at: http://localhost:8000/?wsdl")
+
+    server = make_server('127.0.0.1', 8000, application)
+    server.serve_forever()
diff --git a/setup.cfg b/setup.cfg
index 3cbf7f8..3128c8e 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -1,9 +1,40 @@
+[bumpversion]
+current_version = 0.27.0
+commit = true
+tag = true
+tag_name = {new_version}
+
+[tool:pytest]
+minversion = 3.0
+strict = true
+testpaths = tests
+
 [wheel]
 universal = 1
 
 [flake8]
 max-line-length = 99
 
+[bumpversion:file:setup.py]
+
+[bumpversion:file:docs/conf.py]
+
+[bumpversion:file:src/zeep/__init__.py]
+
+[coverage:run]
+branch = True
+source = 
+	zeep
+
+[coverage:paths]
+source = 
+	src/zeep
+	.tox/*/lib/python*/site-packages/zeep
+	.tox/pypy*/site-packages/zeep
+
+[coverage:report]
+show_missing = True
+
 [egg_info]
 tag_build = 
 tag_date = 0
diff --git a/setup.py b/setup.py
index ea27113..d75137a 100755
--- a/setup.py
+++ b/setup.py
@@ -1,4 +1,5 @@
 import re
+
 from setuptools import find_packages, setup
 
 install_requires = [
@@ -8,6 +9,7 @@ install_requires = [
     'isodate>=0.5.4',
     'lxml>=3.0.0',
     'requests>=2.7.0',
+    'requests-toolbelt>=0.7.0',
     'six>=1.9.0',
     'pytz',
 ]
@@ -16,17 +18,25 @@ docs_require = [
     'sphinx>=1.4.0',
 ]
 
+async_require = [
+    'aiohttp>=1.0',
+]
+
+xmlsec_require = [
+    'xmlsec>=0.6.1',
+]
+
 tests_require = [
-    'freezegun==0.3.7',
+    'freezegun==0.3.8',
     'mock==2.0.0',
     'pretend==1.0.8',
-    'pytest-cov==2.3.1',
-    'pytest==3.0.2',
+    'pytest-cov==2.4.0',
+    'pytest==3.0.6',
     'requests_mock>=0.7.0',
 
     # Linting
     'isort==4.2.5',
-    'flake8==3.0.3',
+    'flake8==3.2.1',
     'flake8-blind-except==0.1.1',
     'flake8-debugger==1.4.0',
 ]
@@ -37,7 +47,7 @@ with open('README.rst') as fh:
 
 setup(
     name='zeep',
-    version='0.24.0',
+    version='0.27.0',
     description='A modern/fast Python SOAP client based on lxml / requests',
     long_description=long_description,
     author="Michael van Tellingen",
@@ -49,6 +59,8 @@ setup(
     extras_require={
         'docs': docs_require,
         'test': tests_require,
+        'async': async_require,
+        'xmlsec': xmlsec_require,
     },
     entry_points={},
     package_dir={'': 'src'},
@@ -65,6 +77,7 @@ setup(
         'Programming Language :: Python :: 3.3',
         'Programming Language :: Python :: 3.4',
         'Programming Language :: Python :: 3.5',
+        'Programming Language :: Python :: 3.6',
         'Programming Language :: Python :: Implementation :: CPython',
         'Programming Language :: Python :: Implementation :: PyPy',
     ],
diff --git a/src/zeep.egg-info/PKG-INFO b/src/zeep.egg-info/PKG-INFO
index fc65fe8..0dcc777 100644
--- a/src/zeep.egg-info/PKG-INFO
+++ b/src/zeep.egg-info/PKG-INFO
@@ -1,6 +1,6 @@
 Metadata-Version: 1.1
 Name: zeep
-Version: 0.24.0
+Version: 0.27.0
 Summary: A modern/fast Python SOAP client based on lxml / requests
 Home-page: http://docs.python-zeep.org
 Author: Michael van Tellingen
@@ -12,23 +12,14 @@ Description: ========================
         
         A fast and modern Python SOAP client
         
-        | Website: http://docs.python-zeep.org/
-        | IRC: #python-zeep on Freenode
-        
         Highlights:
-         * Modern codebase compatible with Python 2.7, 3.3, 3.4, 3.5 and PyPy
+         * Compatible with Python 2.7, 3.3, 3.4, 3.5, 3.6 and PyPy
          * Build on top of lxml and requests
-         * Supports recursive WSDL and XSD documents.
-         * Supports the xsd:choice and xsd:any elements.
-         * Uses the defusedxml module for handling potential XML security issues
-         * Support for WSSE (UsernameToken only for now)
-         * Experimental support for HTTP bindings
-         * Experimental support for WS-Addressing headers
+         * Support for Soap 1.1, Soap 1.2 and HTTP bindings
+         * Support for WS-Addressing headers
+         * Support for WSSE (UserNameToken / x.509 signing)
          * Experimental support for asyncio via aiohttp (Python 3.5+)
         
-        Features still in development include:
-         * WSSE x.509 support (BinarySecurityToken)
-         * WS Policy support
         
         Please see for more information the documentation at
         http://docs.python-zeep.org/
@@ -56,7 +47,7 @@ Description: ========================
         
         To quickly inspect a WSDL file use::
         
-            python -mzeep <url-to-wsdl>
+            python -m zeep <url-to-wsdl>
         
         
         Please see the documentation at http://docs.python-zeep.org for more
@@ -85,5 +76,6 @@ Classifier: Programming Language :: Python :: 3
 Classifier: Programming Language :: Python :: 3.3
 Classifier: Programming Language :: Python :: 3.4
 Classifier: Programming Language :: Python :: 3.5
+Classifier: Programming Language :: Python :: 3.6
 Classifier: Programming Language :: Python :: Implementation :: CPython
 Classifier: Programming Language :: Python :: Implementation :: PyPy
diff --git a/src/zeep.egg-info/SOURCES.txt b/src/zeep.egg-info/SOURCES.txt
index 6f425bb..af3d3dd 100644
--- a/src/zeep.egg-info/SOURCES.txt
+++ b/src/zeep.egg-info/SOURCES.txt
@@ -3,12 +3,20 @@ LICENSE
 README.rst
 setup.cfg
 setup.py
+examples/async_client.py
+examples/code39.py
+examples/echo_services.py
+examples/eu_vat_service.py
+examples/http_basic_auth.py
+examples/km_to_miles.py
+examples/soap_server.py
 src/zeep/__init__.py
 src/zeep/__main__.py
 src/zeep/cache.py
 src/zeep/client.py
 src/zeep/exceptions.py
 src/zeep/helpers.py
+src/zeep/ns.py
 src/zeep/parser.py
 src/zeep/plugins.py
 src/zeep/transports.py
@@ -25,6 +33,7 @@ src/zeep/asyncio/__init__.py
 src/zeep/asyncio/bindings.py
 src/zeep/asyncio/transport.py
 src/zeep/wsdl/__init__.py
+src/zeep/wsdl/attachments.py
 src/zeep/wsdl/definitions.py
 src/zeep/wsdl/parse.py
 src/zeep/wsdl/utils.py
@@ -38,23 +47,38 @@ src/zeep/wsdl/messages/http.py
 src/zeep/wsdl/messages/mime.py
 src/zeep/wsdl/messages/soap.py
 src/zeep/wsse/__init__.py
+src/zeep/wsse/compose.py
+src/zeep/wsse/signature.py
 src/zeep/wsse/username.py
 src/zeep/wsse/utils.py
 src/zeep/xsd/__init__.py
-src/zeep/xsd/builtins.py
 src/zeep/xsd/const.py
 src/zeep/xsd/context.py
-src/zeep/xsd/elements.py
-src/zeep/xsd/indicators.py
-src/zeep/xsd/parser.py
 src/zeep/xsd/printer.py
 src/zeep/xsd/schema.py
-src/zeep/xsd/types.py
 src/zeep/xsd/utils.py
 src/zeep/xsd/valueobjects.py
 src/zeep/xsd/visitor.py
+src/zeep/xsd/elements/__init__.py
+src/zeep/xsd/elements/any.py
+src/zeep/xsd/elements/attribute.py
+src/zeep/xsd/elements/base.py
+src/zeep/xsd/elements/builtins.py
+src/zeep/xsd/elements/element.py
+src/zeep/xsd/elements/indicators.py
+src/zeep/xsd/elements/references.py
+src/zeep/xsd/types/__init__.py
+src/zeep/xsd/types/any.py
+src/zeep/xsd/types/base.py
+src/zeep/xsd/types/builtins.py
+src/zeep/xsd/types/collection.py
+src/zeep/xsd/types/complex.py
+src/zeep/xsd/types/simple.py
 tests/__init__.py
+tests/cert_valid.pem
+tests/cert_valid_pw.pem
 tests/conftest.py
+tests/test_asyncio_transport.py
 tests/test_cache.py
 tests/test_client.py
 tests/test_client_factory.py
@@ -70,6 +94,7 @@ tests/test_wsdl_messages_document.py
 tests/test_wsdl_messages_http.py
 tests/test_wsdl_messages_rpc.py
 tests/test_wsdl_soap.py
+tests/test_wsse_signature.py
 tests/test_wsse_username.py
 tests/test_wsse_utils.py
 tests/test_xsd.py
@@ -86,6 +111,7 @@ tests/test_xsd_signatures.py
 tests/test_xsd_simple_types.py
 tests/test_xsd_types.py
 tests/test_xsd_union.py
+tests/test_xsd_validation.py
 tests/test_xsd_valueobjects.py
 tests/test_xsd_visitor.py
 tests/utils.py
@@ -99,6 +125,7 @@ tests/integration/test_hello_world_recursive.py
 tests/integration/test_http_post.py
 tests/integration/test_http_post.wsdl
 tests/integration/test_recursive_schema.py
+tests/wsdl_files/claim.wsdl
 tests/wsdl_files/http.wsdl
 tests/wsdl_files/soap-enc.xsd
 tests/wsdl_files/soap.wsdl
diff --git a/src/zeep.egg-info/requires.txt b/src/zeep.egg-info/requires.txt
index b63991d..fe4a79f 100644
--- a/src/zeep.egg-info/requires.txt
+++ b/src/zeep.egg-info/requires.txt
@@ -4,20 +4,27 @@ defusedxml>=0.4.1
 isodate>=0.5.4
 lxml>=3.0.0
 requests>=2.7.0
+requests-toolbelt>=0.7.0
 six>=1.9.0
 pytz
 
+[async]
+aiohttp>=1.0
+
 [docs]
 sphinx>=1.4.0
 
 [test]
-freezegun==0.3.7
+freezegun==0.3.8
 mock==2.0.0
 pretend==1.0.8
-pytest-cov==2.3.1
-pytest==3.0.2
+pytest-cov==2.4.0
+pytest==3.0.6
 requests_mock>=0.7.0
 isort==4.2.5
-flake8==3.0.3
+flake8==3.2.1
 flake8-blind-except==0.1.1
 flake8-debugger==1.4.0
+
+[xmlsec]
+xmlsec>=0.6.1
diff --git a/src/zeep/__init__.py b/src/zeep/__init__.py
index 22d0183..0cb7e04 100644
--- a/src/zeep/__init__.py
+++ b/src/zeep/__init__.py
@@ -1,5 +1,6 @@
 from zeep.client import Client  # noqa
 from zeep.transports import Transport  # noqa
 from zeep.plugins import Plugin  # noqa
+from zeep.xsd.valueobjects import AnyObject  # noqa
 
-__version__ = '0.24.0'
+__version__ = '0.27.0'
diff --git a/src/zeep/__main__.py b/src/zeep/__main__.py
index ae2c3af..ff3d509 100644
--- a/src/zeep/__main__.py
+++ b/src/zeep/__main__.py
@@ -5,8 +5,9 @@ import logging
 import logging.config
 import time
 
+import requests
 from six.moves.urllib.parse import urlparse
-from zeep.cache import InMemoryCache, SqliteCache
+from zeep.cache import SqliteCache
 from zeep.client import Client
 from zeep.transports import Transport
 
@@ -59,17 +60,17 @@ def main(args):
         profile = cProfile.Profile()
         profile.enable()
 
-    cache = SqliteCache() if args.cache else InMemoryCache()
-    transport_kwargs = {'cache': cache}
+    cache = SqliteCache() if args.cache else None
+    session = requests.Session()
 
     if args.no_verify:
-        transport_kwargs['verify'] = False
+        session.verify = False
 
     result = urlparse(args.wsdl_file)
     if result.username or result.password:
-        transport_kwargs['http_auth'] = (result.username, result.password)
+        session.auth = (result.username, result.password)
 
-    transport = Transport(**transport_kwargs)
+    transport = Transport(cache=cache, session=session)
     st = time.time()
     client = Client(args.wsdl_file, transport=transport)
     logger.debug("Loading WSDL took %sms", (time.time() - st) * 1000)
diff --git a/src/zeep/asyncio/transport.py b/src/zeep/asyncio/transport.py
index c487811..6968c2a 100644
--- a/src/zeep/asyncio/transport.py
+++ b/src/zeep/asyncio/transport.py
@@ -3,10 +3,11 @@ Adds asyncio support to Zeep. Contains Python 3.5+ only syntax!
 
 """
 import asyncio
+import logging
 
 import aiohttp
-
 from zeep.transports import Transport
+from zeep.utils import get_version
 from zeep.wsdl.utils import etree_to_string
 
 __all__ = ['AsyncTransport']
@@ -16,21 +17,22 @@ class AsyncTransport(Transport):
     """Asynchronous Transport class using aiohttp."""
     supports_async = True
 
-    def __init__(self, loop, *args, **kwargs):
-        self.loop = loop if loop else asyncio.get_event_loop()
-        super().__init__(*args, **kwargs)
+    def __init__(self, loop, cache=None, timeout=300, operation_timeout=None,
+                 session=None):
 
-    def create_session(self):
-        connector = aiohttp.TCPConnector(verify_ssl=self.http_verify)
+        self.loop = loop if loop else asyncio.get_event_loop()
+        self.cache = cache
+        self.load_timeout = timeout
+        self.operation_timeout = operation_timeout
+        self.logger = logging.getLogger(__name__)
 
-        return aiohttp.ClientSession(
-            connector=connector,
-            loop=self.loop,
-            headers=self.http_headers,
-            auth=self.http_auth)
+        self.session = session or aiohttp.ClientSession(loop=self.loop)
+        self.session._default_headers['User-Agent'] = (
+            'Zeep/%s (www.python-zeep.org)' % (get_version()))
 
     def _load_remote_data(self, url):
         result = None
+
         async def _load_remote_data_async():
             nonlocal result
             with aiohttp.Timeout(self.load_timeout):
diff --git a/src/zeep/client.py b/src/zeep/client.py
index 62bfcec..9c7f30a 100644
--- a/src/zeep/client.py
+++ b/src/zeep/client.py
@@ -5,12 +5,6 @@ from contextlib import contextmanager
 from zeep.transports import Transport
 from zeep.wsdl import Document
 
-NSMAP = {
-    'xsd': 'http://www.w3.org/2001/XMLSchema',
-    'soap': 'http://schemas.xmlsoap.org/wsdl/soap/',
-    'soap-env': 'http://schemas.xmlsoap.org/soap/envelope/',
-}
-
 
 logger = logging.getLogger(__name__)
 
@@ -75,6 +69,21 @@ class Factory(object):
 
 
 class Client(object):
+    """The zeep Client.
+
+
+    :param wsdl:
+    :param wsse:
+    :param transport: Custom transport class.
+    :param service_name: The service name for the service binding. Defaults to
+                         the first service in the WSDL document.
+    :param port_name: The port name for the default binding. Defaults to the
+                      first port defined in the service element in the WSDL
+                      document.
+    :param plugins: a list of Plugin instances
+
+
+    """
 
     def __init__(self, wsdl, wsse=None, transport=None,
                  service_name=None, port_name=None, plugins=None):
@@ -110,14 +119,15 @@ class Client(object):
     def options(self, timeout):
         """Context manager to temporarily overrule various options.
 
-        Example::
+        :param timeout: Set the timeout for POST/GET operations (not used for
+                        loading external WSDL or XSD documents)
+
+        To for example set the timeout to 10 seconds use::
 
             client = zeep.Client('foo.wsdl')
             with client.options(timeout=10):
                 client.service.fast_call()
 
-        :param timeout: Set the timeout for POST/GET operations (not used for
-                        loading external WSDL or XSD documents)
 
         """
         with self.transport._options(timeout=timeout):
@@ -134,19 +144,8 @@ class Client(object):
         if not self.wsdl.services:
             return
 
-        if service_name:
-            service = self.wsdl.services.get(service_name)
-            if not service:
-                raise ValueError("Service not found")
-        else:
-            service = next(iter(self.wsdl.services.values()), None)
-
-        if port_name:
-            port = service.ports.get(port_name)
-            if not port:
-                raise ValueError("Port not found")
-        else:
-            port = list(service.ports.values())[0]
+        service = self._get_service(service_name)
+        port = self._get_port(service, port_name)
         return ServiceProxy(self, port.binding, **port.binding_options)
 
     def create_service(self, binding_name, address):
@@ -164,16 +163,40 @@ class Client(object):
                 "are: %s" % (', '.join(self.wsdl.bindings.keys())))
         return ServiceProxy(self, binding, address=address)
 
+    def create_message(self, operation, service_name=None, port_name=None,
+                       args=None, kwargs=None):
+        """Create the payload for the given operation."""
+        service = self._get_service(service_name)
+        port = self._get_port(service, port_name)
+
+        args = args or tuple()
+        kwargs = kwargs or {}
+        envelope, http_headers = port.binding._create(operation, args, kwargs)
+        return envelope
+
     def type_factory(self, namespace):
+        """Return a type factory for the given namespace.
+
+        Example::
+
+            factory = client.type_factory('ns0')
+            user = factory.User(name='John')
+
+        """
         return Factory(self.wsdl.types, 'type', namespace)
 
     def get_type(self, name):
+        """Return the type for the given qualified name."""
         return self.wsdl.types.get_type(name)
 
     def get_element(self, name):
+        """Return the element for the given qualified name."""
         return self.wsdl.types.get_element(name)
 
     def set_ns_prefix(self, prefix, namespace):
+        """Set a shortcut for the given namespace.
+
+        """
         self.wsdl.types.set_ns_prefix(prefix, namespace)
 
     def set_default_soapheaders(self, headers):
@@ -186,3 +209,21 @@ class Client(object):
 
         """
         self._default_soapheaders = headers
+
+    def _get_port(self, service, name):
+        if name:
+            port = service.ports.get(name)
+            if not port:
+                raise ValueError("Port not found")
+        else:
+            port = list(service.ports.values())[0]
+        return port
+
+    def _get_service(self, name):
+        if name:
+            service = self.wsdl.services.get(name)
+            if not service:
+                raise ValueError("Service not found")
+        else:
+            service = next(iter(self.wsdl.services.values()), None)
+        return service
diff --git a/src/zeep/exceptions.py b/src/zeep/exceptions.py
index bdd2334..0056057 100644
--- a/src/zeep/exceptions.py
+++ b/src/zeep/exceptions.py
@@ -1,5 +1,5 @@
 class Error(Exception):
-    def __init__(self, message):
+    def __init__(self, message=''):
         super(Exception, self).__init__(message)
         self.message = message
 
@@ -12,7 +12,18 @@ class XMLSyntaxError(Error):
 
 
 class XMLParseError(Error):
-    pass
+    def __init__(self, *args, **kwargs):
+        self.filename = kwargs.pop('filename', None)
+        self.sourceline = kwargs.pop('sourceline', None)
+        super(XMLParseError, self).__init__(*args, **kwargs)
+
+    def __str__(self):
+        location = None
+        if self.filename and self.sourceline:
+            location = '%s:%s' % (self.filename, self.sourceline)
+        if location:
+            return '%s (%s)' % (self.message, location)
+        return self.message
 
 
 class UnexpectedElementError(Error):
@@ -47,3 +58,19 @@ class Fault(Error):
 
 class ZeepWarning(RuntimeWarning):
     pass
+
+
+class ValidationError(Error):
+    def __init__(self, *args, **kwargs):
+        self.path = kwargs.pop('path', [])
+        super(ValidationError, self).__init__(*args, **kwargs)
+
+    def __str__(self):
+        if self.path:
+            path = '.'.join(str(x) for x in self.path)
+            return '%s (%s)' % (self.message, path)
+        return self.message
+
+
+class SignatureVerificationFailed(Error):
+    pass
diff --git a/src/zeep/helpers.py b/src/zeep/helpers.py
index eec2e69..187626d 100644
--- a/src/zeep/helpers.py
+++ b/src/zeep/helpers.py
@@ -1,25 +1,77 @@
+import datetime
 from collections import OrderedDict
 
 from lxml import etree
 
+from zeep import xsd
 from zeep.xsd.valueobjects import CompoundValue
 
 
-def serialize_object(obj):
+def serialize_object(obj, target_cls=OrderedDict):
     """Serialize zeep objects to native python data structures"""
-    if obj is None:
-        return obj
+    if isinstance(obj, list):
+        return [serialize_object(sub, target_cls) for sub in obj]
 
-    if isinstance(obj, etree._Element):
-        return obj
+    if isinstance(obj, (dict, CompoundValue)):
+        result = target_cls()
+        for key in obj:
+            result[key] = serialize_object(obj[key], target_cls)
+        return result
 
-    if isinstance(obj, list):
-        return [serialize_object(sub) for sub in obj]
-
-    result = OrderedDict()
-    for key in obj:
-        value = obj[key]
-        if isinstance(value, (list, CompoundValue)):
-            value = serialize_object(value)
-        result[key] = value
-    return result
+    return obj
+
+
+def create_xml_soap_map(values):
+    """Create an http://xml.apache.org/xml-soap#Map value."""
+    Map = xsd.ComplexType(
+        xsd.Sequence([
+            xsd.Element(
+                'item',
+                xsd.AnyType(),
+                min_occurs=1,
+                max_occurs="unbounded"),
+            ]),
+        qname=etree.QName('{http://xml.apache.org/xml-soap}Map'))
+
+    KeyValueData = xsd.Element(
+        '{http://xml.apache.org/xml-soap}KeyValueData',
+        xsd.ComplexType(
+            xsd.Sequence([
+                xsd.Element(
+                    'key',
+                    xsd.AnyType(),
+                ),
+                xsd.Element(
+                    'value',
+                    xsd.AnyType(),
+                ),
+            ]),
+        ),
+    )
+
+    return Map(item=[
+        KeyValueData(
+            xsd.AnyObject(xsd.String(), key),
+            xsd.AnyObject(guess_xsd_type(value), value)
+        ) for key, value in values.items()
+    ])
+
+
+def guess_xsd_type(obj):
+    """Return the XSD Type for the given object"""
+    if isinstance(obj, bool):
+        return xsd.Boolean()
+    if isinstance(obj, int):
+        return xsd.Integer()
+    if isinstance(obj, float):
+        return xsd.Float()
+    if isinstance(obj, datetime.datetime):
+        return xsd.DateTime()
+    if isinstance(obj, datetime.date):
+        return xsd.Date()
+    return xsd.String()
+
+
+def Nil():
+    """Return an xsi:nil element"""
+    return xsd.AnyObject(None, None)
diff --git a/src/zeep/ns.py b/src/zeep/ns.py
new file mode 100644
index 0000000..66e1470
--- /dev/null
+++ b/src/zeep/ns.py
@@ -0,0 +1,18 @@
+
+SOAP_11 = 'http://schemas.xmlsoap.org/wsdl/soap/'
+SOAP_12 = 'http://schemas.xmlsoap.org/wsdl/soap12/'
+SOAP_ENV_11 = 'http://schemas.xmlsoap.org/soap/envelope/'
+SOAP_ENV_12 = 'http://www.w3.org/2003/05/soap-envelope'
+
+XSD = 'http://www.w3.org/2001/XMLSchema'
+
+WSDL = 'http://schemas.xmlsoap.org/wsdl/'
+HTTP = 'http://schemas.xmlsoap.org/wsdl/http/'
+MIME = 'http://schemas.xmlsoap.org/wsdl/mime/'
+
+WSA = 'http://www.w3.org/2005/08/addressing'
+
+
+DS = 'http://www.w3.org/2000/09/xmldsig#'
+WSSE = 'http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd'
+WSU = 'http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd'
diff --git a/src/zeep/parser.py b/src/zeep/parser.py
index 4589def..4d6082f 100644
--- a/src/zeep/parser.py
+++ b/src/zeep/parser.py
@@ -8,7 +8,8 @@ from zeep.exceptions import XMLSyntaxError
 
 
 def parse_xml(content, base_url=None, recover=False):
-    parser = etree.XMLParser(remove_comments=True, recover=recover)
+    parser = etree.XMLParser(
+        remove_comments=True, recover=recover, resolve_entities=False)
     try:
         return fromstring(content, parser=parser, base_url=base_url)
     except etree.XMLSyntaxError as exc:
@@ -24,7 +25,7 @@ def load_external(url, transport, base_url=None):
 
 
 def absolute_location(location, base):
-    if location == base or location.startswith('intschema'):
+    if location == base:
         return location
 
     if urlparse(location).scheme in ('http', 'https'):
@@ -36,5 +37,13 @@ def absolute_location(location, base):
         if os.path.isabs(location):
             return location
         if base:
-            return os.path.join(os.path.dirname(base), location)
+            return os.path.realpath(
+                os.path.join(os.path.dirname(base), location))
     return location
+
+
+def is_relative_path(value):
+    """Check if the given value is a relative path"""
+    if urlparse(value).scheme in ('http', 'https', 'file'):
+        return False
+    return not os.path.isabs(value)
diff --git a/src/zeep/plugins.py b/src/zeep/plugins.py
index 5ddd953..c470b56 100644
--- a/src/zeep/plugins.py
+++ b/src/zeep/plugins.py
@@ -5,9 +5,25 @@ class Plugin(object):
     """Base plugin"""
 
     def ingress(self, envelope, http_headers, operation):
+        """Override to update the envelope or http headers when receiving a
+        message.
+
+        :param envelope: The envelope as XML node
+        :param http_headers: Dict with the HTTP headers
+
+        """
         return envelope, http_headers
 
     def egress(self, envelope, http_headers, operation, binding_options):
+        """Override to update the envelope or http headers when sending a
+        message.
+
+        :param envelope: The envelope as XML node
+        :param http_headers: Dict with the HTTP headers
+        :param operation: The associated Operation instance
+        :param binding_options: Binding specific options for the operation
+
+        """
         return envelope, http_headers
 
 
diff --git a/src/zeep/transports.py b/src/zeep/transports.py
index a4d71ed..89fd183 100644
--- a/src/zeep/transports.py
+++ b/src/zeep/transports.py
@@ -5,45 +5,32 @@ from contextlib import contextmanager
 import requests
 
 from six.moves.urllib.parse import urlparse
-from zeep.cache import SqliteCache
-from zeep.utils import NotSet, get_version
+from zeep.utils import get_version
 from zeep.wsdl.utils import etree_to_string
 
 
 class Transport(object):
-    supports_async = False
+    """The transport object handles all communication to the SOAP server.
 
-    def __init__(self, cache=NotSet, timeout=300, operation_timeout=None,
-                 verify=True, http_auth=None):
-        """The transport object handles all communication to the SOAP server.
+    :param cache: The cache object to be used to cache GET requests
+    :param timeout: The timeout for loading wsdl and xsd documents.
+    :param operation_timeout: The timeout for operations (POST/GET). By
+                              default this is None (no timeout).
+    :param session: A request.Session() object (optional)
 
-        :param cache: The cache object to be used to cache GET requests
-        :param timeout: The timeout for loading wsdl and xsd documents.
-        :param operation_timeout: The timeout for operations (POST/GET). By
-                                  default this is None (no timeout).
-        :param verify: Boolean to indicate if the SSL certificate needs to be
-                       verified.
-        :param http_auth: HTTP authentication, passed to requests.
+    """
+    supports_async = False
 
-        """
-        self.cache = SqliteCache() if cache is NotSet else cache
+    def __init__(self, cache=None, timeout=300, operation_timeout=None,
+                 session=None):
+        self.cache = cache
         self.load_timeout = timeout
         self.operation_timeout = operation_timeout
         self.logger = logging.getLogger(__name__)
 
-        self.http_verify = verify
-        self.http_auth = http_auth
-        self.http_headers = {
-            'User-Agent': 'Zeep/%s (www.python-zeep.org)' % (get_version())
-        }
-        self.session = self.create_session()
-
-    def create_session(self):
-        session = requests.Session()
-        session.verify = self.http_verify
-        session.auth = self.http_auth
-        session.headers = self.http_headers
-        return session
+        self.session = session or requests.Session()
+        self.session.headers['User-Agent'] = (
+            'Zeep/%s (www.python-zeep.org)' % (get_version()))
 
     def get(self, address, params, headers):
         """Proxy to requests.get()
@@ -81,9 +68,12 @@ class Transport(object):
             timeout=self.operation_timeout)
 
         if self.logger.isEnabledFor(logging.DEBUG):
-            log_message = response.content
-            if isinstance(log_message, bytes):
-                log_message = log_message.decode('utf-8')
+            if 'multipart/related' in response.headers.get('Content-Type'):
+                log_message = response.content
+            else:
+                log_message = response.content
+                if isinstance(log_message, bytes):
+                    log_message = log_message.decode('utf-8')
 
             self.logger.debug(
                 "HTTP Response from %s (status: %d):\n%s",
diff --git a/src/zeep/utils.py b/src/zeep/utils.py
index 531c7f2..d7efe90 100644
--- a/src/zeep/utils.py
+++ b/src/zeep/utils.py
@@ -1,17 +1,8 @@
 import inspect
 
-
 from lxml import etree
 
 
-class _NotSetClass(object):
-    def __repr__(self):
-        return 'NotSet'
-
-
-NotSet = _NotSetClass()
-
-
 def qname_attr(node, attr_name, target_namespace=None):
     value = node.get(attr_name)
     if value is not None:
@@ -65,3 +56,8 @@ def get_base_class(objects):
             break
         base_class = bases[0][i]
     return base_class
+
+
+def detect_soap_env(envelope):
+    root_tag = etree.QName(envelope)
+    return root_tag.namespace
diff --git a/src/zeep/wsa.py b/src/zeep/wsa.py
index f406934..3c3e653 100644
--- a/src/zeep/wsa.py
+++ b/src/zeep/wsa.py
@@ -3,15 +3,16 @@ import uuid
 from lxml import etree
 from lxml.builder import ElementMaker
 
+from zeep import ns
 from zeep.plugins import Plugin
 from zeep.wsdl.utils import get_or_create_header
 
-WSA = ElementMaker(namespace='http://www.w3.org/2005/08/addressing')
+WSA = ElementMaker(namespace=ns.WSA, nsmap={'wsa': ns.WSA})
 
 
 class WsAddressingPlugin(Plugin):
     nsmap = {
-        'wsa': 'http://www.w3.org/2005/08/addressing'
+        'wsa': ns.WSA
     }
 
     def egress(self, envelope, http_headers, operation, binding_options):
diff --git a/src/zeep/wsdl/attachments.py b/src/zeep/wsdl/attachments.py
new file mode 100644
index 0000000..408ae4d
--- /dev/null
+++ b/src/zeep/wsdl/attachments.py
@@ -0,0 +1,63 @@
+"""Basic implementation to support SOAP-Attachments
+
+See https://www.w3.org/TR/SOAP-attachments
+
+"""
+import base64
+from io import BytesIO
+
+from cached_property import cached_property
+from requests.structures import CaseInsensitiveDict
+
+
+class MessagePack(object):
+    def __init__(self, parts):
+        self._parts = parts
+
+    def __repr__(self):
+        return '<MessagePack(attachments=[%s])>' % (
+            ', '.join(repr(a) for a in self.attachments))
+
+    @property
+    def root(self):
+        return self._root
+
+    def _set_root(self, root):
+        self._root = root
+
+    @cached_property
+    def attachments(self):
+        return [Attachment(part) for part in self._parts]
+
+    def get_by_content_id(self, content_id):
+        for attachment in self.attachments:
+            if attachment.content_id == content_id:
+                return attachment
+
+
+class Attachment(object):
+    def __init__(self, part):
+
+        self.headers = CaseInsensitiveDict({
+            k.decode(part.encoding): v.decode(part.encoding)
+            for k, v in part.headers.items()
+        })
+        self.content_type = self.headers.get('Content-Type', None)
+        self.content_id = self.headers.get('Content-ID', None)
+        self.content_location = self.headers.get('Content-Location', None)
+        self._part = part
+
+    def __repr__(self):
+        return '<Attachment(%r, %r)>' % (self.content_id, self.content_type)
+
+    @cached_property
+    def content(self):
+        encoding = self.headers.get('Content-Transfer-Encoding', None)
+        content = self._part.content
+
+        if encoding == 'base64':
+            return base64.b64decode(content)
+        elif encoding == 'binary':
+            return content
+        else:
+            return content
diff --git a/src/zeep/wsdl/bindings/http.py b/src/zeep/wsdl/bindings/http.py
index e5dc8f8..53a8acf 100644
--- a/src/zeep/wsdl/bindings/http.py
+++ b/src/zeep/wsdl/bindings/http.py
@@ -3,6 +3,7 @@ import logging
 import six
 from lxml import etree
 
+from zeep import ns
 from zeep.exceptions import Fault
 from zeep.utils import qname_attr
 from zeep.wsdl import messages
@@ -11,9 +12,9 @@ from zeep.wsdl.definitions import Binding, Operation
 logger = logging.getLogger(__name__)
 
 NSMAP = {
-    'http': 'http://schemas.xmlsoap.org/wsdl/http/',
-    'wsdl': 'http://schemas.xmlsoap.org/wsdl/',
-    'mime': 'http://schemas.xmlsoap.org/wsdl/mime/',
+    'http': ns.HTTP,
+    'wsdl': ns.WSDL,
+    'mime': ns.MIME,
 }
 
 
@@ -114,7 +115,7 @@ class HttpGetBinding(HttpBinding):
         :type node: lxml.etree._Element
 
         """
-        http_node = node.find(etree.QName(NSMAP['http'], 'binding'))
+        http_node = node.find(etree.QName(ns.HTTP, 'binding'))
         return http_node is not None and http_node.get('verb') == 'GET'
 
 
@@ -158,13 +159,13 @@ class HttpOperation(Operation):
                 message_node = node.getchildren()[0]
             message_class = None
             if message_node is not None:
-                if message_node.tag == etree.QName(NSMAP['http'], 'urlEncoded'):
+                if message_node.tag == etree.QName(ns.HTTP, 'urlEncoded'):
                     message_class = messages.UrlEncoded
-                elif message_node.tag == etree.QName(NSMAP['http'], 'urlReplacement'):
+                elif message_node.tag == etree.QName(ns.HTTP, 'urlReplacement'):
                     message_class = messages.UrlReplacement
-                elif message_node.tag == etree.QName(NSMAP['mime'], 'content'):
+                elif message_node.tag == etree.QName(ns.MIME, 'content'):
                     message_class = messages.MimeContent
-                elif message_node.tag == etree.QName(NSMAP['mime'], 'mimeXml'):
+                elif message_node.tag == etree.QName(ns.MIME, 'mimeXml'):
                     message_class = messages.MimeXML
 
             if message_class:
diff --git a/src/zeep/wsdl/bindings/soap.py b/src/zeep/wsdl/bindings/soap.py
index 2f86319..6ba92e1 100644
--- a/src/zeep/wsdl/bindings/soap.py
+++ b/src/zeep/wsdl/bindings/soap.py
@@ -1,14 +1,16 @@
 import logging
 
 from lxml import etree
+from requests_toolbelt.multipart.decoder import MultipartDecoder
 
-from zeep import plugins, wsa
+from zeep import ns, plugins, wsa
 from zeep.exceptions import Fault, TransportError, XMLSyntaxError
 from zeep.parser import parse_xml
 from zeep.utils import as_qname, qname_attr
+from zeep.wsdl.attachments import MessagePack
 from zeep.wsdl.definitions import Binding, Operation
 from zeep.wsdl.messages import DocumentMessage, RpcMessage
-from zeep.wsdl.utils import etree_to_string
+from zeep.wsdl.utils import etree_to_string, url_http_to_https
 
 logger = logging.getLogger(__name__)
 
@@ -83,7 +85,7 @@ class SoapBinding(Binding):
 
             # Apply WSSE
             if client.wsse:
-                envelope, http_headers = client.wsse.sign(envelope, http_headers)
+                envelope, http_headers = client.wsse.apply(envelope, http_headers)
         return envelope, http_headers
 
     def send(self, client, options, operation, args, kwargs):
@@ -128,8 +130,18 @@ class SoapBinding(Binding):
                 u'Server returned HTTP status %d (no content available)'
                 % response.status_code)
 
+        content_type = response.headers.get('Content-Type', 'text/xml')
+        if 'multipart/related' in content_type:
+            decoder = MultipartDecoder(response.content, content_type, 'utf-8')
+            content = decoder.parts[0].content
+            if len(decoder.parts) > 1:
+                message_pack = MessagePack(parts=decoder.parts[1:])
+        else:
+            content = response.content
+            message_pack = None
+
         try:
-            doc = parse_xml(response.content, recover=True)
+            doc = parse_xml(content)
         except XMLSyntaxError:
             raise TransportError(
                 u'Server returned HTTP status %d (%s)'
@@ -148,7 +160,12 @@ class SoapBinding(Binding):
         if response.status_code != 200 or fault_node is not None:
             return self.process_error(doc, operation)
 
-        return operation.process_reply(doc)
+        result = operation.process_reply(doc)
+
+        if message_pack:
+            message_pack._set_root(result)
+            return message_pack
+        return result
 
     def process_error(self, doc, operation):
         raise NotImplementedError
@@ -158,9 +175,10 @@ class SoapBinding(Binding):
 
         # Force the usage of HTTPS when the force_https boolean is true
         location = address_node.get('location')
-        if force_https and location and location.startswith('http://'):
-            logger.warning("Forcing soap:address location to HTTPS")
-            location = 'https://' + location[7:]
+        if force_https and location:
+            location = url_http_to_https(location)
+            if location != address_node.get('location'):
+                logger.warning("Forcing soap:address location to HTTPS")
 
         return {
             'address': location
@@ -207,10 +225,10 @@ class SoapBinding(Binding):
 
 class Soap11Binding(SoapBinding):
     nsmap = {
-        'soap': 'http://schemas.xmlsoap.org/wsdl/soap/',
-        'soap-env': 'http://schemas.xmlsoap.org/soap/envelope/',
-        'wsdl': 'http://schemas.xmlsoap.org/wsdl/',
-        'xsd': 'http://www.w3.org/2001/XMLSchema',
+        'soap': ns.SOAP_11,
+        'soap-env': ns.SOAP_ENV_11,
+        'wsdl': ns.WSDL,
+        'xsd': ns.XSD,
     }
 
     def process_error(self, doc, operation):
@@ -241,10 +259,10 @@ class Soap11Binding(SoapBinding):
 
 class Soap12Binding(SoapBinding):
     nsmap = {
-        'soap': 'http://schemas.xmlsoap.org/wsdl/soap12/',
-        'soap-env': 'http://www.w3.org/2003/05/soap-envelope',
-        'wsdl': 'http://schemas.xmlsoap.org/wsdl/',
-        'xsd': 'http://www.w3.org/2001/XMLSchema',
+        'soap': ns.SOAP_12,
+        'soap-env': ns.SOAP_ENV_12,
+        'wsdl': ns.WSDL,
+        'xsd': ns.XSD,
     }
 
     def process_error(self, doc, operation):
diff --git a/src/zeep/wsdl/messages/mime.py b/src/zeep/wsdl/messages/mime.py
index ba28522..d509212 100644
--- a/src/zeep/wsdl/messages/mime.py
+++ b/src/zeep/wsdl/messages/mime.py
@@ -2,7 +2,7 @@ import six
 from defusedxml.lxml import fromstring
 from lxml import etree
 
-from zeep import xsd
+from zeep import ns, xsd
 from zeep.helpers import serialize_object
 from zeep.wsdl.messages.base import ConcreteMessage, SerializedMessage
 from zeep.wsdl.utils import etree_to_string
@@ -16,7 +16,7 @@ __all__ = [
 
 class MimeMessage(ConcreteMessage):
     _nsmap = {
-        'mime': 'http://schemas.xmlsoap.org/wsdl/mime/',
+        'mime': ns.MIME,
     }
 
     def __init__(self, wsdl, name, operation, part_name):
diff --git a/src/zeep/wsdl/messages/soap.py b/src/zeep/wsdl/messages/soap.py
index 3875ecf..17a2d2d 100644
--- a/src/zeep/wsdl/messages/soap.py
+++ b/src/zeep/wsdl/messages/soap.py
@@ -272,8 +272,12 @@ class SoapMessage(ConcreteMessage):
         """
         all_elements = xsd.Sequence([
             xsd.Element('body', self.body.type),
-            xsd.Element('header', self.header.type),
         ])
+
+        if self.header.type._element:
+            all_elements.append(
+                xsd.Element('header', self.header.type))
+
         return xsd.Element('envelope', xsd.ComplexType(all_elements))
 
     def _serialize_header(self, headers_value, nsmap):
@@ -299,8 +303,12 @@ class SoapMessage(ConcreteMessage):
                 raise ValueError(
                     "_soapheaders only accepts a dictionary if the wsdl "
                     "defines the headers.")
+
+            # Only render headers for which we have a value
             headers_value = self.header(**headers_value)
-            self.header.type.render(header, headers_value)
+            for name, elm in self.header.type.elements:
+                if name in headers_value and headers_value[name] is not None:
+                    elm.render(header, headers_value[name], ['header', name])
         else:
             raise ValueError("Invalid value given to _soapheaders")
 
diff --git a/src/zeep/wsdl/utils.py b/src/zeep/wsdl/utils.py
index f5af860..6785d56 100644
--- a/src/zeep/wsdl/utils.py
+++ b/src/zeep/wsdl/utils.py
@@ -1,12 +1,14 @@
 from lxml import etree
+from six.moves.urllib.parse import urlparse, urlunparse
+
+from zeep.utils import detect_soap_env
 
 
 def get_or_create_header(envelope):
-    # find the namespace of the SOAP Envelope (because it's different for SOAP 1.1 and 1.2)
-    root_tag = etree.QName(envelope)
-    soap_envelope_namespace = root_tag.namespace
+    soap_env = detect_soap_env(envelope)
+
     # look for the Header element and create it if not found
-    header_qname = '{%s}Header' % soap_envelope_namespace
+    header_qname = '{%s}Header' % soap_env
     header = envelope.find(header_qname)
     if header is None:
         header = etree.Element(header_qname)
@@ -17,3 +19,17 @@ def get_or_create_header(envelope):
 def etree_to_string(node):
     return etree.tostring(
         node, pretty_print=True, xml_declaration=True, encoding='utf-8')
+
+
+def url_http_to_https(value):
+    parts = urlparse(value)
+    if parts.scheme != 'http':
+        return value
+
+    # Check if the url contains ':80' and remove it if that is the case
+    netloc_parts = parts.netloc.rsplit(':', 1)
+    if len(netloc_parts) == 2 and netloc_parts[1] == '80':
+        netloc = netloc_parts[0]
+    else:
+        netloc = parts.netloc
+    return urlunparse(('https', netloc) + parts[2:])
diff --git a/src/zeep/wsdl/wsdl.py b/src/zeep/wsdl/wsdl.py
index ce14b66..23a81cb 100644
--- a/src/zeep/wsdl/wsdl.py
+++ b/src/zeep/wsdl/wsdl.py
@@ -2,16 +2,17 @@ from __future__ import print_function
 
 import logging
 import operator
+import os
 from collections import OrderedDict
 
 import six
 from lxml import etree
 
-from zeep.parser import absolute_location, load_external, parse_xml
+from zeep.parser import (
+    absolute_location, is_relative_path, load_external, parse_xml)
 from zeep.utils import findall_multiple_ns
 from zeep.wsdl import parse
 from zeep.xsd import Schema
-from zeep.xsd.context import ParserContext
 
 NSMAP = {
     'wsdl': 'http://schemas.xmlsoap.org/wsdl/',
@@ -35,7 +36,7 @@ class Document(object):
 
     """
 
-    def __init__(self, location, transport):
+    def __init__(self, location, transport, base=None):
         """Initialize a WSDL document.
 
         The root definition properties are exposed as entry points.
@@ -46,15 +47,18 @@ class Document(object):
         :type transport: zeep.transports.Transport
 
         """
-        self.location = location if not hasattr(location, 'read') else None
+        if isinstance(location, six.string_types):
+            if is_relative_path(location):
+                location = os.path.abspath(location)
+            self.location = location
+        else:
+            self.location = base
+
         self.transport = transport
 
         # Dict with all definition objects within this WSDL
         self._definitions = {}
-        self.types = Schema([], transport=self.transport)
-
-        # Dict with internal schema objects, used for lxml.ImportResolver
-        self._parser_context = ParserContext()
+        self.types = Schema([], transport=self.transport, location=self.location)
 
         document = self._load_content(location)
 
@@ -126,6 +130,10 @@ class Document(object):
             return parse_xml(location.read())
         return load_external(location, self.transport, self.location)
 
+    def _add_definition(self, definition):
+        key = (definition.target_namespace, definition.location)
+        self._definitions[key] = definition
+
 
 class Definition(object):
     """The Definition represents one wsdl:definition within a Document."""
@@ -145,7 +153,7 @@ class Definition(object):
         self._resolved_imports = False
 
         self.target_namespace = doc.get('targetNamespace')
-        self.wsdl._definitions[self.target_namespace] = self
+        self.wsdl._add_definition(self)
         self.nsmap = doc.nsmap
 
         # Process the definitions
@@ -218,18 +226,20 @@ class Definition(object):
 
         """
         for import_node in doc.findall("wsdl:import", namespaces=NSMAP):
-            location = import_node.get('location')
             namespace = import_node.get('namespace')
-            if namespace in self.wsdl._definitions:
-                self.imports[namespace] = self.wsdl._definitions[namespace]
+            location = import_node.get('location')
+            location = absolute_location(location, self.location)
+
+            key = (namespace, location)
+            if key in self.wsdl._definitions:
+                self.imports[key] = self.wsdl._definitions[key]
             else:
                 document = self.wsdl._load_content(location)
-                location = absolute_location(location, self.location)
                 if etree.QName(document.tag).localname == 'schema':
                     self.types.add_documents([document], location)
                 else:
                     wsdl = Definition(self.wsdl, document, location)
-                    self.imports[namespace] = wsdl
+                    self.imports[key] = wsdl
 
     def parse_types(self, doc):
         """Return an xsd.Schema() instance for the given wsdl:types element.
diff --git a/src/zeep/wsse/__init__.py b/src/zeep/wsse/__init__.py
index e69de29..6d11ea0 100644
--- a/src/zeep/wsse/__init__.py
+++ b/src/zeep/wsse/__init__.py
@@ -0,0 +1,3 @@
+from .compose import Compose  # noqa
+from .signature import Signature  # noqa
+from .username import UsernameToken  # noqa
diff --git a/src/zeep/wsse/compose.py b/src/zeep/wsse/compose.py
new file mode 100644
index 0000000..c9722ba
--- /dev/null
+++ b/src/zeep/wsse/compose.py
@@ -0,0 +1,12 @@
+class Compose(object):
+    def __init__(self, wsse_objects):
+        self.wsse_objects = wsse_objects
+
+    def apply(self, envelope, headers):
+        for obj in self.wsse_objects:
+            envelope, headers = obj.apply(envelope, headers)
+        return envelope, headers
+
+    def verify(self, envelope):
+        for obj in self.wsse_objects:
+            obj.verify(envelope)
diff --git a/src/zeep/wsse/signature.py b/src/zeep/wsse/signature.py
new file mode 100644
index 0000000..120619a
--- /dev/null
+++ b/src/zeep/wsse/signature.py
@@ -0,0 +1,255 @@
+"""Functions for WS-Security (WSSE) signature creation and verification.
+
+Heavily based on test examples in https://github.com/mehcode/python-xmlsec as
+well as the xmlsec documentation at https://www.aleksey.com/xmlsec/.
+
+Reading the xmldsig, xmlenc, and ws-security standards documents, though
+admittedly painful, will likely assist in understanding the code in this
+module.
+
+"""
+from lxml import etree
+from lxml.etree import QName
+
+try:
+    import xmlsec
+except ImportError:
+    xmlsec = None
+
+from zeep import ns
+from zeep.utils import detect_soap_env
+from zeep.exceptions import SignatureVerificationFailed
+from zeep.wsse.utils import ensure_id, get_security_header
+
+# SOAP envelope
+SOAP_NS = 'http://schemas.xmlsoap.org/soap/envelope/'
+
+
+class Signature(object):
+    """Sign given SOAP envelope with WSSE sig using given key and cert."""
+
+    def __init__(self, key_file, certfile, password=None):
+        check_xmlsec_import()
+
+        self.key_file = key_file
+        self.certfile = certfile
+        self.password = password
+
+    def apply(self, envelope, headers):
+        sign_envelope(envelope, self.key_file, self.certfile, self.password)
+        return envelope, headers
+
+    def verify(self, envelope):
+        verify_envelope(envelope, self.certfile)
+        return envelope
+
+
+def check_xmlsec_import():
+    if xmlsec is None:
+        raise ImportError(
+            "The xmlsec module is required for wsse.Signature()\n" +
+            "You can install xmlsec with: pip install xmlsec\n" +
+            "or install zeep via: pip install zeep[xmlsec]\n"
+        )
+
+
+def sign_envelope(envelope, keyfile, certfile, password=None):
+    """Sign given SOAP envelope with WSSE sig using given key and cert.
+
+    Sign the wsu:Timestamp node in the wsse:Security header and the soap:Body;
+    both must be present.
+
+    Add a ds:Signature node in the wsse:Security header containing the
+    signature.
+
+    Use EXCL-C14N transforms to normalize the signed XML (so that irrelevant
+    whitespace or attribute ordering changes don't invalidate the
+    signature). Use SHA1 signatures.
+
+    Expects to sign an incoming document something like this (xmlns attributes
+    omitted for readability):
+
+    <soap:Envelope>
+      <soap:Header>
+        <wsse:Security mustUnderstand="true">
+          <wsu:Timestamp>
+            <wsu:Created>2015-06-25T21:53:25.246276+00:00</wsu:Created>
+            <wsu:Expires>2015-06-25T21:58:25.246276+00:00</wsu:Expires>
+          </wsu:Timestamp>
+        </wsse:Security>
+      </soap:Header>
+      <soap:Body>
+        ...
+      </soap:Body>
+    </soap:Envelope>
+
+    After signing, the sample document would look something like this (note the
+    added wsu:Id attr on the soap:Body and wsu:Timestamp nodes, and the added
+    ds:Signature node in the header, with ds:Reference nodes with URI attribute
+    referencing the wsu:Id of the signed nodes):
+
+    <soap:Envelope>
+      <soap:Header>
+        <wsse:Security mustUnderstand="true">
+          <Signature xmlns="http://www.w3.org/2000/09/xmldsig#">
+            <SignedInfo>
+              <CanonicalizationMethod
+                  Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
+              <SignatureMethod
+                  Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"/>
+              <Reference URI="#id-d0f9fd77-f193-471f-8bab-ba9c5afa3e76">
+                <Transforms>
+                  <Transform
+                      Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
+                </Transforms>
+                <DigestMethod
+                    Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/>
+                <DigestValue>nnjjqTKxwl1hT/2RUsBuszgjTbI=</DigestValue>
+              </Reference>
+              <Reference URI="#id-7c425ac1-534a-4478-b5fe-6cae0690f08d">
+                <Transforms>
+                  <Transform
+                      Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
+                </Transforms>
+                <DigestMethod
+                    Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/>
+                <DigestValue>qAATZaSqAr9fta9ApbGrFWDuCCQ=</DigestValue>
+              </Reference>
+            </SignedInfo>
+            <SignatureValue>Hz8jtQb...bOdT6ZdTQ==</SignatureValue>
+            <KeyInfo>
+              <wsse:SecurityTokenReference>
+                <X509Data>
+                  <X509Certificate>MIIDnzC...Ia2qKQ==</X509Certificate>
+                  <X509IssuerSerial>
+                    <X509IssuerName>...</X509IssuerName>
+                    <X509SerialNumber>...</X509SerialNumber>
+                  </X509IssuerSerial>
+                </X509Data>
+              </wsse:SecurityTokenReference>
+            </KeyInfo>
+          </Signature>
+          <wsu:Timestamp wsu:Id="id-7c425ac1-534a-4478-b5fe-6cae0690f08d">
+            <wsu:Created>2015-06-25T22:00:29.821700+00:00</wsu:Created>
+            <wsu:Expires>2015-06-25T22:05:29.821700+00:00</wsu:Expires>
+          </wsu:Timestamp>
+        </wsse:Security>
+      </soap:Header>
+      <soap:Body wsu:Id="id-d0f9fd77-f193-471f-8bab-ba9c5afa3e76">
+        ...
+      </soap:Body>
+    </soap:Envelope>
+
+    """
+    # Create the Signature node.
+    signature = xmlsec.template.create(
+        envelope,
+        xmlsec.Transform.EXCL_C14N,
+        xmlsec.Transform.RSA_SHA1,
+    )
+
+    # Add a KeyInfo node with X509Data child to the Signature. XMLSec will fill
+    # in this template with the actual certificate details when it signs.
+    key_info = xmlsec.template.ensure_key_info(signature)
+    x509_data = xmlsec.template.add_x509_data(key_info)
+    xmlsec.template.x509_data_add_issuer_serial(x509_data)
+    xmlsec.template.x509_data_add_certificate(x509_data)
+
+    # Load the signing key and certificate.
+    key = xmlsec.Key.from_file(keyfile, xmlsec.KeyFormat.PEM, password=password)
+    key.load_cert_from_file(certfile, xmlsec.KeyFormat.PEM)
+
+    # Insert the Signature node in the wsse:Security header.
+    security = get_security_header(envelope)
+    security.insert(0, signature)
+
+    # Perform the actual signing.
+    ctx = xmlsec.SignatureContext()
+    ctx.key = key
+
+    security.append(etree.Element(QName(ns.WSU, 'Timestamp')))
+
+    soap_env = detect_soap_env(envelope)
+    _sign_node(ctx, signature, envelope.find(QName(soap_env, 'Body')))
+    _sign_node(ctx, signature, security.find(QName(ns.WSU, 'Timestamp')))
+
+    ctx.sign(signature)
+
+    # Place the X509 data inside a WSSE SecurityTokenReference within
+    # KeyInfo. The recipient expects this structure, but we can't rearrange
+    # like this until after signing, because otherwise xmlsec won't populate
+    # the X509 data (because it doesn't understand WSSE).
+    sec_token_ref = etree.SubElement(
+        key_info, QName(ns.WSSE, 'SecurityTokenReference'))
+    sec_token_ref.append(x509_data)
+
+
+def verify_envelope(envelope, certfile):
+    """Verify WS-Security signature on given SOAP envelope with given cert.
+
+    Expects a document like that found in the sample XML in the ``sign()``
+    docstring.
+
+    Raise SignatureValidationFailed on failure, silent on success.
+
+    """
+    soap_env = detect_soap_env(envelope)
+
+    header = envelope.find(QName(soap_env, 'Header'))
+    security = header.find(QName(ns.WSSE, 'Security'))
+    signature = security.find(QName(ns.DS, 'Signature'))
+
+    ctx = xmlsec.SignatureContext()
+
+    # Find each signed element and register its ID with the signing context.
+    refs = signature.xpath(
+        'ds:SignedInfo/ds:Reference', namespaces={'ds': ns.DS})
+    for ref in refs:
+        # Get the reference URI and cut off the initial '#'
+        referenced_id = ref.get('URI')[1:]
+        referenced = envelope.xpath(
+            "//*[@wsu:Id='%s']" % referenced_id,
+            namespaces={'wsu': ns.WSU},
+        )[0]
+        ctx.register_id(referenced, 'Id', ns.WSU)
+
+    key = xmlsec.Key.from_file(certfile, xmlsec.KeyFormat.CERT_PEM, None)
+    ctx.key = key
+
+    try:
+        ctx.verify(signature)
+    except xmlsec.Error:
+        # Sadly xmlsec gives us no details about the reason for the failure, so
+        # we have nothing to pass on except that verification failed.
+        raise SignatureVerificationFailed()
+
+
+def _sign_node(ctx, signature, target):
+    """Add sig for ``target`` in ``signature`` node, using ``ctx`` context.
+
+    Doesn't actually perform the signing; ``ctx.sign(signature)`` should be
+    called later to do that.
+
+    Adds a Reference node to the signature with URI attribute pointing to the
+    target node, and registers the target node's ID so XMLSec will be able to
+    find the target node by ID when it signs.
+
+    """
+
+    # Ensure the target node has a wsu:Id attribute and get its value.
+    node_id = ensure_id(target)
+
+    # Unlike HTML, XML doesn't have a single standardized Id. WSSE suggests the
+    # use of the wsu:Id attribute for this purpose, but XMLSec doesn't
+    # understand that natively. So for XMLSec to be able to find the referenced
+    # node by id, we have to tell xmlsec about it using the register_id method.
+    ctx.register_id(target, 'Id', ns.WSU)
+
+    # Add reference to signature with URI attribute pointing to that ID.
+    ref = xmlsec.template.add_reference(
+        signature, xmlsec.Transform.SHA1, uri='#' + node_id)
+    # This is an XML normalization transform which will be performed on the
+    # target node contents before signing. This ensures that changes to
+    # irrelevant whitespace, attribute ordering, etc won't invalidate the
+    # signature.
+    xmlsec.template.add_transform(ref, xmlsec.Transform.EXCL_C14N)
diff --git a/src/zeep/wsse/username.py b/src/zeep/wsse/username.py
index 5fb0081..378f817 100644
--- a/src/zeep/wsse/username.py
+++ b/src/zeep/wsse/username.py
@@ -2,17 +2,9 @@ import base64
 import hashlib
 import os
 
-from lxml.builder import ElementMaker
-
+from zeep import ns
 from zeep.wsse import utils
 
-NSMAP = {
-    'wsse': 'http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd',
-    'wsu': 'http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd',
-}
-WSSE = ElementMaker(namespace=NSMAP['wsse'])
-WSU = ElementMaker(namespace=NSMAP['wsu'])
-
 
 class UsernameToken(object):
     """UsernameToken Profile 1.1
@@ -54,19 +46,19 @@ class UsernameToken(object):
         self.created = created
         self.use_digest = use_digest
 
-    def sign(self, envelope, headers):
+    def apply(self, envelope, headers):
         security = utils.get_security_header(envelope)
 
         # The token placeholder might already exists since it is specified in
         # the WSDL.
-        token = security.find('{%s}UsernameToken' % NSMAP['wsse'])
+        token = security.find('{%s}UsernameToken' % ns.WSSE)
         if token is None:
-            token = WSSE.UsernameToken()
+            token = utils.WSSE.UsernameToken()
             security.append(token)
 
         # Create the sub elements of the UsernameToken element
         elements = [
-            WSSE.Username(self.username)
+            utils.WSSE.Username(self.username)
         ]
         if self.password is not None or self.password_digest is not None:
             if self.use_digest:
@@ -82,7 +74,7 @@ class UsernameToken(object):
 
     def _create_password_text(self):
         return [
-            WSSE.Password(
+            utils.WSSE.Password(
                 self.password,
                 Type='%s#PasswordText' % self.username_token_profile_ns)
         ]
@@ -106,13 +98,13 @@ class UsernameToken(object):
             digest = self.password_digest
 
         return [
-            WSSE.Password(
+            utils.WSSE.Password(
                 digest,
                 Type='%s#PasswordDigest' % self.username_token_profile_ns
             ),
-            WSSE.Nonce(
+            utils.WSSE.Nonce(
                 base64.b64encode(nonce).decode('utf-8'),
                 EncodingType='%s#Base64Binary' % self.soap_message_secutity_ns
             ),
-            WSU.Created(timestamp)
+            utils.WSU.Created(timestamp)
         ]
diff --git a/src/zeep/wsse/utils.py b/src/zeep/wsse/utils.py
index e7ed5e2..d08cfc1 100644
--- a/src/zeep/wsse/utils.py
+++ b/src/zeep/wsse/utils.py
@@ -1,14 +1,20 @@
+from uuid import uuid4
+from lxml import etree
 import datetime
 
 import pytz
 from lxml.builder import ElementMaker
 
+from zeep import ns
 from zeep.wsdl.utils import get_or_create_header
 
 NSMAP = {
-    'wsse': 'http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd',
+    'wsse': ns.WSSE,
+    'wsu': ns.WSU,
 }
-WSSE = ElementMaker(namespace=NSMAP['wsse'])
+WSSE = ElementMaker(namespace=NSMAP['wsse'], nsmap={'wsse': ns.WSSE})
+WSU = ElementMaker(namespace=NSMAP['wsu'], nsmap={'wsu': ns.WSU})
+ID_ATTR = etree.QName(NSMAP['wsu'], 'Id')
 
 
 def get_security_header(doc):
@@ -28,3 +34,21 @@ def get_timestamp(timestamp=None):
     timestamp = timestamp or datetime.datetime.utcnow()
     timestamp = timestamp.replace(tzinfo=pytz.utc, microsecond=0)
     return timestamp.isoformat()
+
+
+def get_unique_id():
+    return 'id-{0}'.format(uuid4())
+
+
+def ensure_id(node):
+    """Ensure given node has a wsu:Id attribute; add unique one if not.
+
+    Return found/created attribute value.
+
+    """
+    assert node is not None
+    id_val = node.get(ID_ATTR)
+    if not id_val:
+        id_val = get_unique_id()
+        node.set(ID_ATTR, id_val)
+    return id_val
diff --git a/src/zeep/xsd/__init__.py b/src/zeep/xsd/__init__.py
index 65cce78..1fc975d 100644
--- a/src/zeep/xsd/__init__.py
+++ b/src/zeep/xsd/__init__.py
@@ -1,6 +1,6 @@
-from zeep.xsd.builtins import *  # noqa
+from zeep.xsd.const import SkipValue  # noqa
 from zeep.xsd.elements import *  # noqa
+from zeep.xsd.schema import Schema  # noqa
 from zeep.xsd.types import *  # noqa
+from zeep.xsd.types.builtins import *  # noqa
 from zeep.xsd.valueobjects import *  # noqa
-from zeep.xsd.schema import Schema  # noqa
-from zeep.xsd.indicators import *  # noqa
diff --git a/src/zeep/xsd/const.py b/src/zeep/xsd/const.py
index a2b4a36..7ba420c 100644
--- a/src/zeep/xsd/const.py
+++ b/src/zeep/xsd/const.py
@@ -10,3 +10,15 @@ def xsi_ns(localname):
 
 def xsd_ns(localname):
     return etree.QName(NS_XSD, localname)
+
+
+class _StaticIdentity(object):
+    def __init__(self, val):
+        self.__value__ = val
+
+    def __repr__(self):
+        return self.__value__
+
+
+NotSet = _StaticIdentity('NotSet')
+SkipValue = _StaticIdentity('SkipValue')
diff --git a/src/zeep/xsd/context.py b/src/zeep/xsd/context.py
index 744f665..7863516 100644
--- a/src/zeep/xsd/context.py
+++ b/src/zeep/xsd/context.py
@@ -1,47 +1,3 @@
-class SchemaRepository(object):
-    """Mapping between schema target namespace and schema object"""
-    def __init__(self):
-        self._schemas = {}
-
-    def add(self, schema):
-        self._schemas[schema._target_namespace] = schema
-
-    def get(self, namespace):
-        if namespace in self._schemas:
-            return self._schemas[namespace]
-
-    def __contains__(self, namespace):
-        return namespace in self._schemas
-
-    def __len__(self):
-        return len(self._schemas)
-
-
-class SchemaNodeRepository(object):
-    """Mapping between schema target namespace and lxml node"""
-    def __init__(self):
-        self._nodes = {}
-
-    def add(self, key, value):
-        self._nodes[key] = value
-
-    def get(self, key):
-        return self._nodes[key]
-
-    def __len__(self):
-        return len(self._nodes)
-
-
-class ParserContext(object):
-    """Parser context when parsing wsdl/xsd files"""
-    def __init__(self):
-        self.schema_nodes = SchemaNodeRepository()
-        self.schema_objects = SchemaRepository()
-
-        # Mapping between internal nodes and original location
-        self.schema_locations = {}
-
-
 class XmlParserContext(object):
     """Parser context when parsing XML elements"""
 
diff --git a/src/zeep/xsd/elements.py b/src/zeep/xsd/elements.py
deleted file mode 100644
index 8a62188..0000000
--- a/src/zeep/xsd/elements.py
+++ /dev/null
@@ -1,500 +0,0 @@
-import copy
-import logging
-
-from lxml import etree
-
-from zeep import exceptions
-from zeep.exceptions import UnexpectedElementError
-from zeep.utils import qname_attr
-from zeep.xsd.const import xsi_ns
-from zeep.xsd.context import XmlParserContext
-from zeep.xsd.utils import max_occurs_iter
-from zeep.xsd.valueobjects import AnyObject  # cyclic import / FIXME
-
-logger = logging.getLogger(__name__)
-
-
-class Base(object):
-
-    @property
-    def accepts_multiple(self):
-        return self.max_occurs != 1
-
-    @property
-    def default_value(self):
-        return None
-
-    @property
-    def is_optional(self):
-        return self.min_occurs == 0
-
-    def parse_args(self, args, index=0):
-        result = {}
-        if not args:
-            return result, args, index
-
-        value = args[index]
-        index += 1
-        return {self.attr_name: value}, args, index
-
-    def parse_kwargs(self, kwargs, name, available_kwargs):
-        raise NotImplementedError()
-
-    def parse_xmlelements(self, xmlelements, schema, name=None, context=None):
-        """Consume matching xmlelements and call parse() on each of them"""
-        raise NotImplementedError()
-
-    def signature(self, depth=()):
-        return ''
-
-
-class Any(Base):
-    name = None
-
-    def __init__(self, max_occurs=1, min_occurs=1, process_contents='strict',
-                 restrict=None):
-        """
-
-        :param process_contents: Specifies how the XML processor should handle
-                                 validation against the elements specified by
-                                 this any element
-        :type process_contents: str (strict, lax, skip)
-
-        """
-        super(Any, self).__init__()
-        self.max_occurs = max_occurs
-        self.min_occurs = min_occurs
-        self.restrict = restrict
-        self.process_contents = process_contents
-
-        # cyclic import
-        from zeep.xsd.builtins import AnyType
-        self.type = AnyType()
-
-    def __call__(self, any_object):
-        return any_object
-
-    def __repr__(self):
-        return '<%s(name=%r)>' % (self.__class__.__name__, self.name)
-
-    def accept(self, value):
-        return True
-
-    def parse(self, xmlelement, schema, context=None):
-        if self.process_contents == 'skip':
-            return xmlelement
-
-        qname = etree.QName(xmlelement.tag)
-        for context_schema in context.schemas:
-            if qname.namespace in context_schema._schemas:
-                schema = context_schema
-                break
-
-        xsd_type = qname_attr(xmlelement, xsi_ns('type'))
-        if xsd_type is not None:
-            xsd_type = schema.get_type(xsd_type)
-            return xsd_type.parse_xmlelement(xmlelement, schema, context=context)
-
-        try:
-            element = schema.get_element(xmlelement.tag)
-            return element.parse(xmlelement, schema, context=context)
-        except (exceptions.NamespaceError, exceptions.LookupError):
-            return xmlelement
-
-    def parse_kwargs(self, kwargs, name, available_kwargs):
-        if name in available_kwargs:
-            available_kwargs.remove(name)
-            value = kwargs[name]
-            return {name: value}
-        return {}
-
-    def parse_xmlelements(self, xmlelements, schema, name=None, context=None):
-        """Consume matching xmlelements and call parse() on each of them"""
-        result = []
-
-        for i in max_occurs_iter(self.max_occurs):
-            if xmlelements:
-                xmlelement = xmlelements.popleft()
-                item = self.parse(xmlelement, schema, context=context)
-                if item is not None:
-                    result.append(item)
-            else:
-                break
-
-        if not self.accepts_multiple:
-            result = result[0] if result else None
-        return result
-
-    def render(self, parent, value):
-        assert parent is not None
-        if self.accepts_multiple and isinstance(value, list):
-            from zeep.xsd import SimpleType
-
-            if isinstance(self.restrict, SimpleType):
-                for val in value:
-                    node = etree.SubElement(parent, 'item')
-                    node.set(xsi_ns('type'), self.restrict.qname)
-                    self._render_value_item(node, val)
-            elif self.restrict:
-                for val in value:
-                    node = etree.SubElement(parent, self.restrict.name)
-                    # node.set(xsi_ns('type'), self.restrict.qname)
-                    self._render_value_item(node, val)
-            else:
-                for val in value:
-                    self._render_value_item(parent, val)
-        else:
-            self._render_value_item(parent, value)
-
-    def _render_value_item(self, parent, value):
-        if value is None:  # can be an lxml element
-            return
-
-        # Check if we received a proper value object. If we receive the wrong
-        # type then return a nice error message
-        if self.restrict:
-            expected_types = (etree._Element,) + self.restrict.accepted_types
-        else:
-            expected_types = (etree._Element, AnyObject)
-
-        if not isinstance(value, expected_types):
-            type_names = [
-                '%s.%s' % (t.__module__, t.__name__) for t in expected_types
-            ]
-            err_message = "Any element received object of type %r, expected %s" % (
-                type(value).__name__, ' or '.join(type_names))
-
-            raise TypeError('\n'.join((
-                err_message,
-                "See http://docs.python-zeep.org/en/master/datastructures.html"
-                "#any-objects for more information"
-            )))
-
-        if isinstance(value, etree._Element):
-            parent.append(value)
-
-        elif self.restrict:
-            if isinstance(value, list):
-                for val in value:
-                    self.restrict.render(parent, val)
-            else:
-                self.restrict.render(parent, value)
-        else:
-            if isinstance(value.value, list):
-                for val in value.value:
-                    value.xsd_elm.render(parent, val)
-            else:
-                value.xsd_elm.render(parent, value.value)
-
-    def resolve(self):
-        return self
-
-    def signature(self, depth=()):
-        if self.restrict:
-            base = self.restrict.name
-        else:
-            base = 'ANY'
-
-        if self.accepts_multiple:
-            return '%s[]' % base
-        return base
-
-
-class Element(Base):
-    def __init__(self, name, type_=None, min_occurs=1, max_occurs=1,
-                 nillable=False, default=None, is_global=False, attr_name=None):
-        if name is None:
-            raise ValueError("name cannot be None", self.__class__)
-        if not isinstance(name, etree.QName):
-            name = etree.QName(name)
-
-        self.name = name.localname if name else None
-        self.qname = name
-        self.type = type_
-        self.min_occurs = min_occurs
-        self.max_occurs = max_occurs
-        self.nillable = nillable
-        self.is_global = is_global
-        self.default = default
-        self.attr_name = attr_name or self.name
-        # assert type_
-
-    def __str__(self):
-        if self.type:
-            return '%s(%s)' % (self.name, self.type.signature())
-        return '%s()' % self.name
-
-    def __call__(self, *args, **kwargs):
-        instance = self.type(*args, **kwargs)
-        if hasattr(instance, '_xsd_type'):
-            instance._xsd_elm = self
-        return instance
-
-    def __repr__(self):
-        return '<%s(name=%r, type=%r)>' % (
-            self.__class__.__name__, self.name, self.type)
-
-    def __eq__(self, other):
-        return (
-            other is not None and
-            self.__class__ == other.__class__ and
-            self.__dict__ == other.__dict__)
-
-    @property
-    def default_value(self):
-        value = [] if self.accepts_multiple else self.default
-        return value
-
-    def clone(self, name=None, min_occurs=1, max_occurs=1):
-        new = copy.copy(self)
-
-        if name:
-            if not isinstance(name, etree.QName):
-                name = etree.QName(name)
-            new.name = name.localname
-            new.qname = name
-            new.attr_name = new.name
-
-        new.min_occurs = min_occurs
-        new.max_occurs = max_occurs
-        return new
-
-    def parse(self, xmlelement, schema, allow_none=False, context=None):
-        """Process the given xmlelement. If it has an xsi:type attribute then
-        use that for further processing. This should only be done for subtypes
-        of the defined type but for now we just accept everything.
-
-        """
-        context = context or XmlParserContext()
-        instance_type = qname_attr(xmlelement, xsi_ns('type'))
-        xsd_type = None
-        if instance_type:
-            xsd_type = schema.get_type(instance_type, fail_silently=True)
-        xsd_type = xsd_type or self.type
-        return xsd_type.parse_xmlelement(
-            xmlelement, schema, allow_none=allow_none, context=context)
-
-    def parse_kwargs(self, kwargs, name, available_kwargs):
-        return self.type.parse_kwargs(
-            kwargs, name or self.attr_name, available_kwargs)
-
-    def parse_xmlelements(self, xmlelements, schema, name=None, context=None):
-        """Consume matching xmlelements and call parse() on each of them"""
-        result = []
-        num_matches = 0
-        for i in max_occurs_iter(self.max_occurs):
-            if not xmlelements:
-                break
-
-            # Workaround for SOAP servers which incorrectly use unqualified
-            # or qualified elements in the responses (#170, #176). To make the
-            # best of it we compare the full uri's if both elements have a
-            # namespace. If only one has a namespace then only compare the
-            # localname.
-
-            # If both elements have a namespace and they don't match then skip
-            element_tag = etree.QName(xmlelements[0].tag)
-            if (
-                element_tag.namespace and self.qname.namespace and
-                element_tag.namespace != self.qname.namespace
-            ):
-                break
-
-            # Only compare the localname
-            if element_tag.localname == self.qname.localname:
-                xmlelement = xmlelements.popleft()
-                num_matches += 1
-                item = self.parse(
-                    xmlelement, schema, allow_none=True, context=context)
-                if item is not None:
-                    result.append(item)
-            else:
-                # If the element passed doesn't match and the current one is
-                # not optional then throw an error
-                if num_matches == 0 and not self.is_optional:
-                    raise UnexpectedElementError(
-                        "Unexpected element %r, expected %r" % (
-                            element_tag.text, self.qname.text))
-                break
-
-        if not self.accepts_multiple:
-            result = result[0] if result else None
-        return result
-
-    def render(self, parent, value):
-        """Render the value(s) on the parent lxml.Element.
-
-        This actually just calls _render_value_item for each value.
-
-        """
-        assert parent is not None
-
-        if self.accepts_multiple and isinstance(value, list):
-            for val in value:
-                self._render_value_item(parent, val)
-        else:
-            self._render_value_item(parent, value)
-
-    def _render_value_item(self, parent, value):
-        """Render the value on the parent lxml.Element"""
-        if value is None:
-            if self.is_optional:
-                return
-
-            elm = etree.SubElement(parent, self.qname)
-            if self.nillable:
-                elm.set(xsi_ns('nil'), 'true')
-            return
-
-        node = etree.SubElement(parent, self.qname)
-        xsd_type = getattr(value, '_xsd_type', self.type)
-
-        if xsd_type != self.type:
-            return value._xsd_type.render(node, value, xsd_type)
-        return self.type.render(node, value)
-
-    def resolve_type(self):
-        self.type = self.type.resolve()
-
-    def resolve(self):
-        self.resolve_type()
-        return self
-
-    def signature(self, depth=()):
-        if len(depth) > 0 and self.is_global:
-            return self.name + '()'
-
-        value = self.type.signature(depth)
-        if self.accepts_multiple:
-            return '%s[]' % value
-        return value
-
-
-class Attribute(Element):
-    def __init__(self, name, type_=None, required=False, default=None):
-        super(Attribute, self).__init__(name=name, type_=type_, default=default)
-        self.required = required
-        self.array_type = None
-
-    def parse(self, value):
-        try:
-            return self.type.pythonvalue(value)
-        except (TypeError, ValueError):
-            logger.exception("Error during xml -> python translation")
-            return None
-
-    def render(self, parent, value):
-        if value is None and not self.required:
-            return
-
-        value = self.type.xmlvalue(value)
-        parent.set(self.qname, value)
-
-    def clone(self, *args, **kwargs):
-        array_type = kwargs.pop('array_type', None)
-        new = super(Attribute, self).clone(*args, **kwargs)
-        new.array_type = array_type
-        return new
-
-    def resolve(self):
-        retval = super(Attribute, self).resolve()
-        self.type = self.type.resolve()
-        if self.array_type:
-            retval.array_type = self.array_type.resolve()
-        return retval
-
-
-class AttributeGroup(Element):
-    def __init__(self, name, attributes):
-        self.name = name
-        self.type = None
-        self._attributes = attributes
-        super(AttributeGroup, self).__init__(name, is_global=True)
-
-    @property
-    def attributes(self):
-        result = []
-        for attr in self._attributes:
-            if isinstance(attr, AttributeGroup):
-                result.extend(attr.attributes)
-            else:
-                result.append(attr)
-        return result
-
-    def resolve(self):
-        resolved = []
-        for attribute in self._attributes:
-            value = attribute.resolve()
-            assert value is not None
-            if isinstance(value, list):
-                resolved.extend(value)
-            else:
-                resolved.append(value)
-        self._attributes = resolved
-        return self
-
-    def signature(self, depth=()):
-        return ', '.join(attr.signature() for attr in self._attributes)
-
-
-class AnyAttribute(Base):
-    name = None
-
-    def __init__(self, process_contents='strict'):
-        self.qname = None
-        self.process_contents = process_contents
-
-    def parse(self, attributes, context=None):
-        return attributes
-
-    def resolve(self):
-        return self
-
-    def render(self, parent, value):
-        if value is None:
-            return
-
-        for name, val in value.items():
-            parent.set(name, val)
-
-    def signature(self, depth=()):
-        return '{}'
-
-
-class RefElement(object):
-
-    def __init__(self, tag, ref, schema, is_qualified=False,
-                 min_occurs=1, max_occurs=1):
-        self._ref = ref
-        self._is_qualified = is_qualified
-        self._schema = schema
-        self.min_occurs = min_occurs
-        self.max_occurs = max_occurs
-
-    def resolve(self):
-        elm = self._schema.get_element(self._ref)
-        elm = elm.clone(
-            elm.qname, min_occurs=self.min_occurs, max_occurs=self.max_occurs)
-        return elm.resolve()
-
-
-class RefAttribute(RefElement):
-    def __init__(self, *args, **kwargs):
-        self._array_type = kwargs.pop('array_type', None)
-        super(RefAttribute, self).__init__(*args, **kwargs)
-
-    def resolve(self):
-        attrib = self._schema.get_attribute(self._ref)
-        attrib = attrib.clone(attrib.qname, array_type=self._array_type)
-        return attrib.resolve()
-
-
-class RefAttributeGroup(RefElement):
-    def resolve(self):
-        value = self._schema.get_attribute_group(self._ref)
-        return value.resolve()
-
-
-class RefGroup(RefElement):
-    def resolve(self):
-        return self._schema.get_group(self._ref)
diff --git a/src/zeep/xsd/elements/__init__.py b/src/zeep/xsd/elements/__init__.py
new file mode 100644
index 0000000..4978477
--- /dev/null
+++ b/src/zeep/xsd/elements/__init__.py
@@ -0,0 +1,5 @@
+from .any import *  # noqa
+from .attribute import *  # noqa
+from .element import *  # noqa
+from .indicators import *  # noqa
+from .references import *  # noqa
diff --git a/src/zeep/xsd/elements/any.py b/src/zeep/xsd/elements/any.py
new file mode 100644
index 0000000..d91c506
--- /dev/null
+++ b/src/zeep/xsd/elements/any.py
@@ -0,0 +1,223 @@
+import logging
+
+from lxml import etree
+
+from zeep import exceptions
+from zeep.utils import qname_attr
+from zeep.xsd.const import xsi_ns, NotSet
+from zeep.xsd.elements.base import Base
+from zeep.xsd.utils import max_occurs_iter
+from zeep.xsd.valueobjects import AnyObject
+
+logger = logging.getLogger(__name__)
+
+
+__all__ = ['Any', 'AnyAttribute']
+
+
+class Any(Base):
+    name = None
+
+    def __init__(self, max_occurs=1, min_occurs=1, process_contents='strict',
+                 restrict=None):
+        """
+
+        :param process_contents: Specifies how the XML processor should handle
+                                 validation against the elements specified by
+                                 this any element
+        :type process_contents: str (strict, lax, skip)
+
+        """
+        super(Any, self).__init__()
+        self.max_occurs = max_occurs
+        self.min_occurs = min_occurs
+        self.restrict = restrict
+        self.process_contents = process_contents
+
+        # cyclic import
+        from zeep.xsd import AnyType
+        self.type = AnyType()
+
+    def __call__(self, any_object):
+        return any_object
+
+    def __repr__(self):
+        return '<%s(name=%r)>' % (self.__class__.__name__, self.name)
+
+    def accept(self, value):
+        return True
+
+    def parse(self, xmlelement, schema, context=None):
+        if self.process_contents == 'skip':
+            return xmlelement
+
+        # If a schema was passed inline then check for a matching one
+        qname = etree.QName(xmlelement.tag)
+        if context and context.schemas:
+            for context_schema in context.schemas:
+                if context_schema._has_schema_document(qname.namespace):
+                    schema = context_schema
+                    break
+
+        # Lookup type via xsi:type attribute
+        xsd_type = qname_attr(xmlelement, xsi_ns('type'))
+        if xsd_type is not None:
+            xsd_type = schema.get_type(xsd_type)
+            return xsd_type.parse_xmlelement(xmlelement, schema, context=context)
+
+        # Check if a restrict is used
+        if self.restrict:
+            return self.restrict.parse_xmlelement(
+                xmlelement, schema, context=context)
+
+        try:
+            element = schema.get_element(xmlelement.tag)
+            return element.parse(xmlelement, schema, context=context)
+        except (exceptions.NamespaceError, exceptions.LookupError):
+            return xmlelement
+
+    def parse_kwargs(self, kwargs, name, available_kwargs):
+        if name in available_kwargs:
+            available_kwargs.remove(name)
+            value = kwargs[name]
+            return {name: value}
+        return {}
+
+    def parse_xmlelements(self, xmlelements, schema, name=None, context=None):
+        """Consume matching xmlelements and call parse() on each of them"""
+        result = []
+
+        for _unused in max_occurs_iter(self.max_occurs):
+            if xmlelements:
+                xmlelement = xmlelements.popleft()
+                item = self.parse(xmlelement, schema, context=context)
+                if item is not None:
+                    result.append(item)
+            else:
+                break
+
+        if not self.accepts_multiple:
+            result = result[0] if result else None
+        return result
+
+    def render(self, parent, value, render_path=None):
+        assert parent is not None
+        self.validate(value, render_path)
+
+        if self.accepts_multiple and isinstance(value, list):
+            from zeep.xsd import AnySimpleType
+
+            if isinstance(self.restrict, AnySimpleType):
+                for val in value:
+                    node = etree.SubElement(parent, 'item')
+                    node.set(xsi_ns('type'), self.restrict.qname)
+                    self._render_value_item(node, val, render_path)
+            elif self.restrict:
+                for val in value:
+                    node = etree.SubElement(parent, self.restrict.name)
+                    # node.set(xsi_ns('type'), self.restrict.qname)
+                    self._render_value_item(node, val, render_path)
+            else:
+                for val in value:
+                    self._render_value_item(parent, val, render_path)
+        else:
+            self._render_value_item(parent, value, render_path)
+
+    def _render_value_item(self, parent, value, render_path):
+        if value is None:  # can be an lxml element
+            return
+
+        elif isinstance(value, etree._Element):
+            parent.append(value)
+
+        elif self.restrict:
+            if isinstance(value, list):
+                for val in value:
+                    self.restrict.render(parent, val, None, render_path)
+            else:
+                self.restrict.render(parent, value, None, render_path)
+        else:
+            if isinstance(value.value, list):
+                for val in value.value:
+                    value.xsd_elm.render(parent, val, render_path)
+            else:
+                value.xsd_elm.render(parent, value.value, render_path)
+
+    def validate(self, value, render_path):
+        if self.accepts_multiple and isinstance(value, list):
+
+            # Validate bounds
+            if len(value) < self.min_occurs:
+                raise exceptions.ValidationError(
+                    "Expected at least %d items (minOccurs check)" % self.min_occurs)
+            if self.max_occurs != 'unbounded' and len(value) > self.max_occurs:
+                raise exceptions.ValidationError(
+                    "Expected at most %d items (maxOccurs check)" % self.min_occurs)
+
+            for val in value:
+                self._validate_item(val, render_path)
+        else:
+            if not self.is_optional and value in (None, NotSet):
+                raise exceptions.ValidationError("Missing element for Any")
+
+            self._validate_item(value, render_path)
+
+    def _validate_item(self, value, render_path):
+        if value is None:  # can be an lxml element
+            return
+
+        # Check if we received a proper value object. If we receive the wrong
+        # type then return a nice error message
+        if self.restrict:
+            expected_types = (etree._Element,) + self.restrict.accepted_types
+        else:
+            expected_types = (etree._Element, AnyObject)
+        if not isinstance(value, expected_types):
+            type_names = [
+                '%s.%s' % (t.__module__, t.__name__) for t in expected_types
+            ]
+            err_message = "Any element received object of type %r, expected %s" % (
+                type(value).__name__, ' or '.join(type_names))
+
+            raise TypeError('\n'.join((
+                err_message,
+                "See http://docs.python-zeep.org/en/master/datastructures.html"
+                "#any-objects for more information"
+            )))
+
+    def resolve(self):
+        return self
+
+    def signature(self, depth=()):
+        if self.restrict:
+            base = self.restrict.name
+        else:
+            base = 'ANY'
+
+        if self.accepts_multiple:
+            return '%s[]' % base
+        return base
+
+
+class AnyAttribute(Base):
+    name = None
+
+    def __init__(self, process_contents='strict'):
+        self.qname = None
+        self.process_contents = process_contents
+
+    def parse(self, attributes, context=None):
+        return attributes
+
+    def resolve(self):
+        return self
+
+    def render(self, parent, value, render_path=None):
+        if value is None:
+            return
+
+        for name, val in value.items():
+            parent.set(name, val)
+
+    def signature(self, depth=()):
+        return '{}'
diff --git a/src/zeep/xsd/elements/attribute.py b/src/zeep/xsd/elements/attribute.py
new file mode 100644
index 0000000..04ec751
--- /dev/null
+++ b/src/zeep/xsd/elements/attribute.py
@@ -0,0 +1,92 @@
+import logging
+
+from lxml import etree
+
+from zeep import exceptions
+from zeep.xsd.const import NotSet
+from zeep.xsd.elements.element import Element
+
+logger = logging.getLogger(__name__)
+
+__all__ = ['Attribute', 'AttributeGroup']
+
+
+class Attribute(Element):
+    def __init__(self, name, type_=None, required=False, default=None):
+        super(Attribute, self).__init__(name=name, type_=type_, default=default)
+        self.required = required
+        self.array_type = None
+
+    def parse(self, value):
+        try:
+            return self.type.pythonvalue(value)
+        except (TypeError, ValueError):
+            logger.exception("Error during xml -> python translation")
+            return None
+
+    def render(self, parent, value, render_path=None):
+        if value in (None, NotSet) and not self.required:
+            return
+
+        self.validate(value, render_path)
+
+        value = self.type.xmlvalue(value)
+        parent.set(self.qname, value)
+
+    def validate(self, value, render_path):
+        try:
+            self.type.validate(value, required=self.required)
+        except exceptions.ValidationError as exc:
+            raise exceptions.ValidationError(
+                "The attribute %s is not valid: %s" % (self.qname, exc.message),
+                path=render_path)
+
+    def clone(self, *args, **kwargs):
+        array_type = kwargs.pop('array_type', None)
+        new = super(Attribute, self).clone(*args, **kwargs)
+        new.array_type = array_type
+        return new
+
+    def resolve(self):
+        retval = super(Attribute, self).resolve()
+        self.type = self.type.resolve()
+        if self.array_type:
+            retval.array_type = self.array_type.resolve()
+        return retval
+
+
+class AttributeGroup(object):
+    def __init__(self, name, attributes):
+        if not isinstance(name, etree.QName):
+            name = etree.QName(name)
+
+        self.name = name.localname
+        self.qname = name
+        self.type = None
+        self._attributes = attributes
+        self.is_global = True
+
+    @property
+    def attributes(self):
+        result = []
+        for attr in self._attributes:
+            if isinstance(attr, AttributeGroup):
+                result.extend(attr.attributes)
+            else:
+                result.append(attr)
+        return result
+
+    def resolve(self):
+        resolved = []
+        for attribute in self._attributes:
+            value = attribute.resolve()
+            assert value is not None
+            if isinstance(value, list):
+                resolved.extend(value)
+            else:
+                resolved.append(value)
+        self._attributes = resolved
+        return self
+
+    def signature(self, depth=()):
+        return ', '.join(attr.signature() for attr in self._attributes)
diff --git a/src/zeep/xsd/elements/base.py b/src/zeep/xsd/elements/base.py
new file mode 100644
index 0000000..64ff835
--- /dev/null
+++ b/src/zeep/xsd/elements/base.py
@@ -0,0 +1,32 @@
+class Base(object):
+
+    @property
+    def accepts_multiple(self):
+        return self.max_occurs != 1
+
+    @property
+    def default_value(self):
+        return None
+
+    @property
+    def is_optional(self):
+        return self.min_occurs == 0
+
+    def parse_args(self, args, index=0):
+        result = {}
+        if not args:
+            return result, args, index
+
+        value = args[index]
+        index += 1
+        return {self.attr_name: value}, args, index
+
+    def parse_kwargs(self, kwargs, name, available_kwargs):
+        raise NotImplementedError()
+
+    def parse_xmlelements(self, xmlelements, schema, name=None, context=None):
+        """Consume matching xmlelements and call parse() on each of them"""
+        raise NotImplementedError()
+
+    def signature(self, depth=()):
+        return ''
diff --git a/src/zeep/xsd/elements/builtins.py b/src/zeep/xsd/elements/builtins.py
new file mode 100644
index 0000000..5eca4c6
--- /dev/null
+++ b/src/zeep/xsd/elements/builtins.py
@@ -0,0 +1,40 @@
+from __future__ import division
+
+from zeep.xsd.const import xsd_ns
+from zeep.xsd.elements.base import Base
+
+
+class Schema(Base):
+    name = 'schema'
+    attr_name = 'schema'
+    qname = xsd_ns('schema')
+
+    def clone(self, qname, min_occurs=1, max_occurs=1):
+        return self.__class__()
+
+    def parse_kwargs(self, kwargs, name, available_kwargs):
+        if name in available_kwargs:
+            value = kwargs[name]
+            available_kwargs.remove(name)
+            return {name: value}
+        return {}
+
+    def parse(self, xmlelement, schema, context=None):
+        from zeep.xsd.schema import Schema
+        schema = Schema(xmlelement, schema._transport)
+        context.schemas.append(schema)
+        return schema
+
+    def parse_xmlelements(self, xmlelements, schema, name=None, context=None):
+        if xmlelements[0].tag == self.qname:
+            xmlelement = xmlelements.popleft()
+            result = self.parse(xmlelement, schema, context=context)
+            return result
+
+    def resolve(self):
+        return self
+
+
+default_elements = {
+    xsd_ns('schema'): Schema(),
+}
diff --git a/src/zeep/xsd/elements/element.py b/src/zeep/xsd/elements/element.py
new file mode 100644
index 0000000..ebb1739
--- /dev/null
+++ b/src/zeep/xsd/elements/element.py
@@ -0,0 +1,225 @@
+import copy
+import logging
+
+from lxml import etree
+
+from zeep import exceptions
+from zeep.exceptions import UnexpectedElementError
+from zeep.utils import qname_attr
+from zeep.xsd.const import NotSet, xsi_ns
+from zeep.xsd.context import XmlParserContext
+from zeep.xsd.elements.base import Base
+from zeep.xsd.utils import max_occurs_iter
+
+logger = logging.getLogger(__name__)
+
+__all__ = ['Element']
+
+
+class Element(Base):
+    def __init__(self, name, type_=None, min_occurs=1, max_occurs=1,
+                 nillable=False, default=None, is_global=False, attr_name=None):
+
+        if name is None:
+            raise ValueError("name cannot be None", self.__class__)
+        if not isinstance(name, etree.QName):
+            name = etree.QName(name)
+
+        self.name = name.localname if name else None
+        self.qname = name
+        self.type = type_
+        self.min_occurs = min_occurs
+        self.max_occurs = max_occurs
+        self.nillable = nillable
+        self.is_global = is_global
+        self.default = default
+        self.attr_name = attr_name or self.name
+        # assert type_
+
+    def __str__(self):
+        if self.type:
+            return '%s(%s)' % (self.name, self.type.signature())
+        return '%s()' % self.name
+
+    def __call__(self, *args, **kwargs):
+        instance = self.type(*args, **kwargs)
+        if hasattr(instance, '_xsd_type'):
+            instance._xsd_elm = self
+        return instance
+
+    def __repr__(self):
+        return '<%s(name=%r, type=%r)>' % (
+            self.__class__.__name__, self.name, self.type)
+
+    def __eq__(self, other):
+        return (
+            other is not None and
+            self.__class__ == other.__class__ and
+            self.__dict__ == other.__dict__)
+
+    @property
+    def default_value(self):
+        value = [] if self.accepts_multiple else self.default
+        return value
+
+    def clone(self, name=None, min_occurs=1, max_occurs=1):
+        new = copy.copy(self)
+
+        if name:
+            if not isinstance(name, etree.QName):
+                name = etree.QName(name)
+            new.name = name.localname
+            new.qname = name
+            new.attr_name = new.name
+
+        new.min_occurs = min_occurs
+        new.max_occurs = max_occurs
+        return new
+
+    def parse(self, xmlelement, schema, allow_none=False, context=None):
+        """Process the given xmlelement. If it has an xsi:type attribute then
+        use that for further processing. This should only be done for subtypes
+        of the defined type but for now we just accept everything.
+
+        """
+        context = context or XmlParserContext()
+        instance_type = qname_attr(xmlelement, xsi_ns('type'))
+        xsd_type = None
+        if instance_type:
+            xsd_type = schema.get_type(instance_type, fail_silently=True)
+        xsd_type = xsd_type or self.type
+        return xsd_type.parse_xmlelement(
+            xmlelement, schema, allow_none=allow_none, context=context)
+
+    def parse_kwargs(self, kwargs, name, available_kwargs):
+        return self.type.parse_kwargs(
+            kwargs, name or self.attr_name, available_kwargs)
+
+    def parse_xmlelements(self, xmlelements, schema, name=None, context=None):
+        """Consume matching xmlelements and call parse() on each of them"""
+        result = []
+        num_matches = 0
+        for _unused in max_occurs_iter(self.max_occurs):
+            if not xmlelements:
+                break
+
+            # Workaround for SOAP servers which incorrectly use unqualified
+            # or qualified elements in the responses (#170, #176). To make the
+            # best of it we compare the full uri's if both elements have a
+            # namespace. If only one has a namespace then only compare the
+            # localname.
+
+            # If both elements have a namespace and they don't match then skip
+            element_tag = etree.QName(xmlelements[0].tag)
+            if (
+                element_tag.namespace and self.qname.namespace and
+                element_tag.namespace != self.qname.namespace
+            ):
+                break
+
+            # Only compare the localname
+            if element_tag.localname == self.qname.localname:
+                xmlelement = xmlelements.popleft()
+                num_matches += 1
+                item = self.parse(
+                    xmlelement, schema, allow_none=True, context=context)
+                if item is not None:
+                    result.append(item)
+            else:
+                # If the element passed doesn't match and the current one is
+                # not optional then throw an error
+                if num_matches == 0 and not self.is_optional:
+                    raise UnexpectedElementError(
+                        "Unexpected element %r, expected %r" % (
+                            element_tag.text, self.qname.text))
+                break
+
+        if not self.accepts_multiple:
+            result = result[0] if result else None
+        return result
+
+    def render(self, parent, value, render_path=None):
+        """Render the value(s) on the parent lxml.Element.
+
+        This actually just calls _render_value_item for each value.
+
+        """
+        if not render_path:
+            render_path = [self.qname.localname]
+
+        assert parent is not None
+        self.validate(value, render_path)
+
+        if self.accepts_multiple and isinstance(value, list):
+            for val in value:
+                self._render_value_item(parent, val, render_path)
+        else:
+            self._render_value_item(parent, value, render_path)
+
+    def _render_value_item(self, parent, value, render_path):
+        """Render the value on the parent lxml.Element"""
+        if value is None or value is NotSet:
+            if self.is_optional:
+                return
+
+            elm = etree.SubElement(parent, self.qname)
+            if self.nillable:
+                elm.set(xsi_ns('nil'), 'true')
+            return
+
+        node = etree.SubElement(parent, self.qname)
+        xsd_type = getattr(value, '_xsd_type', self.type)
+
+        if xsd_type != self.type:
+            return value._xsd_type.render(node, value, xsd_type, render_path)
+        return self.type.render(node, value, None, render_path)
+
+    def validate(self, value, render_path=None):
+        """Validate that the value is valid"""
+        if self.accepts_multiple and isinstance(value, list):
+
+            # Validate bounds
+            if len(value) < self.min_occurs:
+                raise exceptions.ValidationError(
+                    "Expected at least %d items (minOccurs check)" % self.min_occurs,
+                    path=render_path)
+            elif self.max_occurs != 'unbounded' and len(value) > self.max_occurs:
+                raise exceptions.ValidationError(
+                    "Expected at most %d items (maxOccurs check)" % self.min_occurs,
+                    path=render_path)
+
+            for val in value:
+                self._validate_item(val, render_path)
+        else:
+            if not self.is_optional and not self.nillable and value in (None, NotSet):
+                raise exceptions.ValidationError(
+                    "Missing element %s" % (self.name), path=render_path)
+
+            self._validate_item(value, render_path)
+
+    def _validate_item(self, value, render_path):
+        if self.nillable and value in (None, NotSet):
+            return
+
+        try:
+            self.type.validate(value, required=True)
+        except exceptions.ValidationError as exc:
+            raise exceptions.ValidationError(
+                "The element %s is not valid: %s" % (self.qname, exc.message),
+                path=render_path)
+
+    def resolve_type(self):
+        self.type = self.type.resolve()
+
+    def resolve(self):
+        self.resolve_type()
+        return self
+
+    def signature(self, depth=()):
+        if len(depth) > 0 and self.is_global:
+            return self.name + '()'
+
+        value = self.type.signature(depth)
+        if self.accepts_multiple:
+            return '%s[]' % value
+        return value
diff --git a/src/zeep/xsd/indicators.py b/src/zeep/xsd/elements/indicators.py
similarity index 94%
rename from src/zeep/xsd/indicators.py
rename to src/zeep/xsd/elements/indicators.py
index ccfe423..282b13a 100644
--- a/src/zeep/xsd/indicators.py
+++ b/src/zeep/xsd/elements/indicators.py
@@ -1,5 +1,3 @@
-from __future__ import print_function
-
 import copy
 import operator
 from collections import OrderedDict, defaultdict, deque
@@ -7,7 +5,9 @@ from collections import OrderedDict, defaultdict, deque
 from cached_property import threaded_cached_property
 
 from zeep.exceptions import UnexpectedElementError
-from zeep.xsd.elements import Any, Base, Element
+from zeep.xsd.const import NotSet, SkipValue
+from zeep.xsd.elements import Any, Element
+from zeep.xsd.elements.base import Base
 from zeep.xsd.utils import (
     NamePrefixGenerator, UniqueNameGenerator, max_occurs_iter)
 
@@ -36,11 +36,8 @@ class OrderIndicator(Indicator, list):
     def __init__(self, elements=None, min_occurs=1, max_occurs=1):
         self.min_occurs = min_occurs
         self.max_occurs = max_occurs
-
-        if elements is None:
-            super(OrderIndicator, self).__init__()
-        else:
-            super(OrderIndicator, self).__init__()
+        super(OrderIndicator, self).__init__()
+        if elements is not None:
             self.extend(elements)
 
     def clone(self, name, min_occurs=1, max_occurs=1):
@@ -125,7 +122,7 @@ class OrderIndicator(Indicator, list):
                 item_kwargs = [item_kwargs]
 
             result = []
-            for i, item_value in zip(max_occurs_iter(self.max_occurs), item_kwargs):
+            for item_value in max_occurs_iter(self.max_occurs, item_kwargs):
                 item_kwargs = set(item_value.keys())
                 subresult = OrderedDict()
                 for item_name, element in self.elements:
@@ -163,24 +160,31 @@ class OrderIndicator(Indicator, list):
             self[i] = elm.resolve()
         return self
 
-    def render(self, parent, value):
+    def render(self, parent, value, render_path):
         """Create subelements in the given parent object."""
         if not isinstance(value, list):
             values = [value]
         else:
             values = value
 
-        for i, value in zip(max_occurs_iter(self.max_occurs), values):
+        for value in max_occurs_iter(self.max_occurs, values):
             for name, element in self.elements_nested:
                 if name:
                     if name in value:
                         element_value = value[name]
+                        child_path = render_path + [name]
                     else:
-                        element_value = None
+                        element_value = NotSet
+                        child_path = render_path
                 else:
                     element_value = value
+                    child_path = render_path
+
+                if element_value is SkipValue:
+                    continue
+
                 if element_value is not None or not element.is_optional:
-                    element.render(parent, element_value)
+                    element.render(parent, element_value, child_path)
 
     def signature(self, depth=()):
         """
@@ -249,7 +253,7 @@ class Choice(OrderIndicator):
         """Return a dictionary"""
         result = []
 
-        for i in max_occurs_iter(self.max_occurs):
+        for _unused in max_occurs_iter(self.max_occurs):
             if not xmlelements:
                 break
 
@@ -374,7 +378,7 @@ class Choice(OrderIndicator):
             result = {name: result}
         return result
 
-    def render(self, parent, value):
+    def render(self, parent, value, render_path):
         """Render the value to the parent element tree node.
 
         This is a bit more complex then the order render methods since we need
@@ -388,7 +392,7 @@ class Choice(OrderIndicator):
             result = self._find_element_to_render(item)
             if result:
                 element, choice_value = result
-                element.render(parent, choice_value)
+                element.render(parent, choice_value, render_path)
 
     def accept(self, values):
         """Return the number of values which are accepted by this choice.
@@ -454,7 +458,7 @@ class Sequence(OrderIndicator):
 
     def parse_xmlelements(self, xmlelements, schema, name=None, context=None):
         result = []
-        for item in max_occurs_iter(self.max_occurs):
+        for _unused in max_occurs_iter(self.max_occurs):
             if not xmlelements:
                 break
 
@@ -527,7 +531,7 @@ class Group(Indicator):
 
             result = []
             sub_name = '_value_1' if self.child.accepts_multiple else None
-            for i, sub_kwargs in zip(max_occurs_iter(self.max_occurs), item_kwargs):
+            for sub_kwargs in max_occurs_iter(self.max_occurs, item_kwargs):
                 available_sub_kwargs = set(sub_kwargs.keys())
                 subresult = self.child.parse_kwargs(
                     sub_kwargs, sub_name, available_sub_kwargs)
@@ -543,7 +547,7 @@ class Group(Indicator):
     def parse_xmlelements(self, xmlelements, schema, name=None, context=None):
         result = []
 
-        for i in max_occurs_iter(self.max_occurs):
+        for _unused in max_occurs_iter(self.max_occurs):
             result.append(
                 self.child.parse_xmlelements(
                     xmlelements, schema, name, context=context)
diff --git a/src/zeep/xsd/elements/references.py b/src/zeep/xsd/elements/references.py
new file mode 100644
index 0000000..d3eb1ce
--- /dev/null
+++ b/src/zeep/xsd/elements/references.py
@@ -0,0 +1,40 @@
+__all__ = ['RefElement', 'RefAttribute', 'RefAttributeGroup', 'RefGroup']
+
+
+class RefElement(object):
+
+    def __init__(self, tag, ref, schema, is_qualified=False,
+                 min_occurs=1, max_occurs=1):
+        self._ref = ref
+        self._is_qualified = is_qualified
+        self._schema = schema
+        self.min_occurs = min_occurs
+        self.max_occurs = max_occurs
+
+    def resolve(self):
+        elm = self._schema.get_element(self._ref)
+        elm = elm.clone(
+            elm.qname, min_occurs=self.min_occurs, max_occurs=self.max_occurs)
+        return elm.resolve()
+
+
+class RefAttribute(RefElement):
+    def __init__(self, *args, **kwargs):
+        self._array_type = kwargs.pop('array_type', None)
+        super(RefAttribute, self).__init__(*args, **kwargs)
+
+    def resolve(self):
+        attrib = self._schema.get_attribute(self._ref)
+        attrib = attrib.clone(attrib.qname, array_type=self._array_type)
+        return attrib.resolve()
+
+
+class RefAttributeGroup(RefElement):
+    def resolve(self):
+        value = self._schema.get_attribute_group(self._ref)
+        return value.resolve()
+
+
+class RefGroup(RefElement):
+    def resolve(self):
+        return self._schema.get_group(self._ref)
diff --git a/src/zeep/xsd/parser.py b/src/zeep/xsd/parser.py
deleted file mode 100644
index fd674d0..0000000
--- a/src/zeep/xsd/parser.py
+++ /dev/null
@@ -1,42 +0,0 @@
-from defusedxml.lxml import fromstring
-from lxml import etree
-
-from six.moves.urllib.parse import urlparse
-from zeep.exceptions import XMLSyntaxError
-from zeep.parser import absolute_location
-
-
-class ImportResolver(etree.Resolver):
-    def __init__(self, transport, parser_context):
-        self.parser_context = parser_context
-        self.transport = transport
-
-    def resolve(self, url, pubid, context):
-        if url.startswith('intschema'):
-            text = etree.tostring(self.parser_context.schema_nodes.get(url))
-            return self.resolve_string(text, context)
-
-        if urlparse(url).scheme in ('http', 'https'):
-            content = self.transport.load(url)
-            return self.resolve_string(content, context)
-
-
-def parse_xml(content, transport, parser_context=None, base_url=None):
-    parser = etree.XMLParser(remove_comments=True)
-    parser.resolvers.add(ImportResolver(transport, parser_context))
-    try:
-        return fromstring(content, parser=parser, base_url=base_url)
-    except etree.XMLSyntaxError as exc:
-        raise XMLSyntaxError("Invalid XML content received (%s)" % exc.message)
-
-
-def load_external(url, transport, parser_context=None, base_url=None):
-    if url.startswith('intschema'):
-        assert parser_context
-        return parser_context.schema_nodes.get(url)
-
-    if base_url:
-        url = absolute_location(url, base_url)
-
-    response = transport.load(url)
-    return parse_xml(response, transport, parser_context, base_url)
diff --git a/src/zeep/xsd/schema.py b/src/zeep/xsd/schema.py
index 3f9da9f..93993c1 100644
--- a/src/zeep/xsd/schema.py
+++ b/src/zeep/xsd/schema.py
@@ -4,9 +4,9 @@ from collections import OrderedDict
 from lxml import etree
 
 from zeep import exceptions
-from zeep.xsd import builtins as xsd_builtins
 from zeep.xsd import const
-from zeep.xsd.context import ParserContext
+from zeep.xsd.elements import builtins as xsd_builtins_elements
+from zeep.xsd.types import builtins as xsd_builtins_types
 from zeep.xsd.visitor import SchemaVisitor
 
 logger = logging.getLogger(__name__)
@@ -15,12 +15,10 @@ logger = logging.getLogger(__name__)
 class Schema(object):
     """A schema is a collection of schema documents."""
 
-    def __init__(self, node=None, transport=None, location=None,
-                 parser_context=None):
-        self._parser_context = parser_context or ParserContext()
+    def __init__(self, node=None, transport=None, location=None):
         self._transport = transport
 
-        self._schemas = OrderedDict()
+        self._documents = OrderedDict()
         self._prefix_map_auto = {}
         self._prefix_map_custom = {}
 
@@ -30,26 +28,11 @@ class Schema(object):
             nodes = node
         self.add_documents(nodes, location)
 
-    def add_documents(self, schema_nodes, location):
-        documents = []
-        for node in schema_nodes:
-            document = SchemaDocument(
-                node, self._transport, self, location,
-                self._parser_context, location)
-            documents.append(document)
-
-        for document in documents:
-            document.resolve()
-
-        self._prefix_map_auto = self._create_prefix_map()
-
-    def __repr__(self):
-        if self._schemas:
-            main_doc = next(iter(self._schemas.values()))
-            location = main_doc._location
-        else:
-            location = '<none>'
-        return '<Schema(location=%r)>' % location
+    @property
+    def documents(self):
+        for documents in self._documents.values():
+            for document in documents:
+                yield document
 
     @property
     def prefix_map(self):
@@ -64,47 +47,59 @@ class Schema(object):
     @property
     def is_empty(self):
         """Boolean to indicate if this schema contains any types or elements"""
-        return all(schema.is_empty for schema in self._schemas.values())
+        return all(document.is_empty for document in self.documents)
 
     @property
     def namespaces(self):
-        return set(self._schemas.keys())
+        return set(self._documents.keys())
 
     @property
     def elements(self):
         """Yield all globla xsd.Type objects"""
-        for schema in self._schemas.values():
-            for element in schema._elements.values():
+        for document in self.documents:
+            for element in document._elements.values():
                 yield element
 
     @property
     def types(self):
         """Yield all globla xsd.Type objects"""
-        for schema in self._schemas.values():
-            for type_ in schema._types.values():
+        for document in self.documents:
+            for type_ in document._types.values():
                 yield type_
 
+    def __repr__(self):
+        if self._documents:
+            main_doc = next(self.documents)
+            location = main_doc._location
+        else:
+            location = '<none>'
+        return '<Schema(location=%r)>' % location
+
+    def add_documents(self, schema_nodes, location):
+        documents = []
+        for node in schema_nodes:
+            document = self.create_new_document(node, location)
+            documents.append(document)
+
+        for document in documents:
+            document.resolve()
+
+        self._prefix_map_auto = self._create_prefix_map()
+
     def get_element(self, qname):
         """Return a global xsd.Element object with the given qname"""
         qname = self._create_qname(qname)
-        if qname.text in xsd_builtins.default_elements:
-            return xsd_builtins.default_elements[qname]
+        if qname.text in xsd_builtins_elements.default_elements:
+            return xsd_builtins_elements.default_elements[qname]
 
         # Handle XSD namespace items
         if qname.namespace == const.NS_XSD:
             try:
-                return xsd_builtins.default_elements[qname]
+                return xsd_builtins_elements.default_elements[qname]
             except KeyError:
                 raise exceptions.LookupError("No such type %r" % qname.text)
 
-        try:
-            schema = self._get_schema_document(qname.namespace)
-            return schema.get_element(qname)
-        except exceptions.NamespaceError:
-            raise exceptions.NamespaceError((
-                "Unable to resolve element %s. " +
-                "No schema available for the namespace %r."
-            ) % (qname.text, qname.namespace))
+        return self._get_instance(qname, 'get_element', 'element')
 
     def get_type(self, qname, fail_silently=False):
         """Return a global xsd.Type object with the given qname"""
@@ -113,66 +108,78 @@ class Schema(object):
         # Handle XSD namespace items
         if qname.namespace == const.NS_XSD:
             try:
-                return xsd_builtins.default_types[qname]
+                return xsd_builtins_types.default_types[qname]
             except KeyError:
                 raise exceptions.LookupError("No such type %r" % qname.text)
 
         try:
-            schema = self._get_schema_document(qname.namespace)
-            return schema.get_type(qname)
-        except exceptions.NamespaceError:
-            message = (
-                "Unable to resolve type %s. " +
-                "No schema available for the namespace %r."
-            ) % (qname.text, qname.namespace)
-
+            return self._get_instance(qname, 'get_type', 'type')
+        except exceptions.NamespaceError as exc:
             if fail_silently:
-                logger.info(message)
+                logger.info(str(exc))
             else:
-                raise exceptions.NamespaceError(message)
+                raise
 
     def get_group(self, qname):
         """Return a global xsd.Group object with the given qname"""
-        qname = self._create_qname(qname)
-        try:
-            schema = self._get_schema_document(qname.namespace)
-            return schema.get_group(qname)
-        except exceptions.NamespaceError:
-            raise exceptions.NamespaceError((
-                "Unable to resolve group %s. " +
-                "No schema available for the namespace %r."
-            ) % (qname.text, qname.namespace))
+        return self._get_instance(qname, 'get_group', 'group')
 
     def get_attribute(self, qname):
         """Return a global xsd.attributeGroup object with the given qname"""
-        qname = self._create_qname(qname)
-        try:
-            schema = self._get_schema_document(qname.namespace)
-            return schema.get_attribute(qname)
-        except exceptions.NamespaceError:
-            raise exceptions.NamespaceError((
-                "Unable to resolve attribute %s. " +
-                "No schema available for the namespace %r."
-            ) % (qname.text, qname.namespace))
+        return self._get_instance(qname, 'get_attribute', 'attribute')
 
     def get_attribute_group(self, qname):
         """Return a global xsd.attributeGroup object with the given qname"""
-        qname = self._create_qname(qname)
+        return self._get_instance(qname, 'get_attribute_group', 'attributeGroup')
+
+    def set_ns_prefix(self, prefix, namespace):
+        self._prefix_map_custom[prefix] = namespace
+
+    def get_ns_prefix(self, prefix):
         try:
-            schema = self._get_schema_document(qname.namespace)
-            return schema.get_attribute_group(qname)
-        except exceptions.NamespaceError:
-            raise exceptions.NamespaceError((
-                "Unable to resolve attributeGroup %s. " +
-                "No schema available for the namespace %r."
-            ) % (qname.text, qname.namespace))
+            try:
+                return self._prefix_map_custom[prefix]
+            except KeyError:
+                return self._prefix_map_auto[prefix]
+        except KeyError:
+            raise ValueError("No such prefix %r" % prefix)
+
+    def create_new_document(self, node, url, base_url=None):
+        namespace = node.get('targetNamespace') if node is not None else None
+        if base_url is None:
+            base_url = url
+
+        schema = SchemaDocument(namespace, url, base_url)
+        self._add_schema_document(schema)
+        schema.load(self, node)
+        return schema
 
     def merge(self, schema):
         """Merge an other XSD schema in this one"""
-        for namespace, _schema in schema._schemas.items():
-            self._schemas[namespace] = _schema
+        for document in schema.documents:
+            self._add_schema_document(document)
         self._prefix_map_auto = self._create_prefix_map()
 
+    def _get_instance(self, qname, method_name, name):
+        """Return an object from one of the SchemaDocument's"""
+        qname = self._create_qname(qname)
+        try:
+            last_exception = None
+            for schema in self._get_schema_documents(qname.namespace):
+                method = getattr(schema, method_name)
+                try:
+                    return method(qname)
+                except exceptions.LookupError as exc:
+                    last_exception = exc
+                    continue
+            raise last_exception
+
+        except exceptions.NamespaceError:
+            raise exceptions.NamespaceError((
+                "Unable to resolve %s %s. " +
+                "No schema available for the namespace %r."
+            ) % (name, qname.text, qname.namespace))
+
     def _create_qname(self, name):
         """Create an `lxml.etree.QName()` object for the given qname string.
 
@@ -198,48 +205,42 @@ class Schema(object):
         prefix_map = {
             'xsd': 'http://www.w3.org/2001/XMLSchema',
         }
-        for i, namespace in enumerate(self._schemas.keys()):
+        for i, namespace in enumerate(self._documents.keys()):
             if namespace is None:
                 continue
             prefix_map['ns%d' % i] = namespace
         return prefix_map
 
-    def set_ns_prefix(self, prefix, namespace):
-        self._prefix_map_custom[prefix] = namespace
-
-    def get_ns_prefix(self, prefix):
-        try:
-            try:
-                return self._prefix_map_custom[prefix]
-            except KeyError:
-                return self._prefix_map_auto[prefix]
-        except KeyError:
-            raise ValueError("No such prefix %r" % prefix)
+    def _has_schema_document(self, namespace):
+        return namespace in self._documents
 
     def _add_schema_document(self, document):
-        logger.info("Add document with tns %s to schema %s", document._target_namespace, id(self))
-        self._schemas[document._target_namespace] = document
+        logger.info("Add document with tns %s to schema %s", document.namespace, id(self))
+        documents = self._documents.setdefault(document.namespace, [])
+        documents.append(document)
+
+    def _get_schema_document(self, namespace, location):
+        for document in self._documents.get(namespace, []):
+            if document._location == location:
+                return document
 
-    def _get_schema_document(self, namespace):
-        if namespace not in self._schemas:
+    def _get_schema_documents(self, namespace, fail_silently=False):
+        if namespace not in self._documents:
+            if fail_silently:
+                return []
             raise exceptions.NamespaceError(
                 "No schema available for the namespace %r" % namespace)
-        return self._schemas[namespace]
+        return self._documents[namespace]
 
 
 class SchemaDocument(object):
-    def __init__(self, node, transport, schema, location, parser_context, base_url):
+    def __init__(self, namespace, location, base_url):
         logger.debug("Init schema document for %r", location)
-        assert node is not None
-        assert parser_context
 
         # Internal
-        self._schema = schema
         self._base_url = base_url or location
         self._location = location
-        self._transport = transport
-        self._target_namespace = (
-            node.get('targetNamespace') if node is not None else None)
+        self._target_namespace = namespace
         self._elm_instances = []
 
         self._attribute_groups = {}
@@ -254,21 +255,33 @@ class SchemaDocument(object):
         self._resolved = False
         # self._xml_schema = None
 
-        self._schema._add_schema_document(self)
-        parser_context.schema_objects.add(self)
-
-        if node is not None:
-            # Disable XML schema validation for now
-            # if len(node) > 0:
-            #     self.xml_schema = etree.XMLSchema(node)
-
-            visitor = SchemaVisitor(self, parser_context)
-            visitor.visit_schema(node)
-
     def __repr__(self):
         return '<SchemaDocument(location=%r, tns=%r, is_empty=%r)>' % (
             self._location, self._target_namespace, self.is_empty)
 
+    @property
+    def namespace(self):
+        return self._target_namespace
+
+    @property
+    def is_empty(self):
+        return not bool(self._imports or self._types or self._elements)
+
+    def load(self, schema, node):
+        if node is None:
+            return
+
+        if not schema._has_schema_document(self._target_namespace):
+            raise RuntimeError(
+                "The document needs to be registered in the schema before " +
+                "it can be loaded")
+
+        # Disable XML schema validation for now
+        # if len(node) > 0:
+        #     self.xml_schema = etree.XMLSchema(node)
+        visitor = SchemaVisitor(schema, self)
+        visitor.visit_schema(node)
+
     def resolve(self):
         logger.info("Resolving in schema %s", self)
 
@@ -276,8 +289,9 @@ class SchemaDocument(object):
             return
         self._resolved = True
 
-        for schema in self._imports.values():
-            schema.resolve()
+        for schemas in self._imports.values():
+            for schema in schemas:
+                schema.resolve()
 
         def _resolve_dict(val):
             for key, obj in val.items():
@@ -295,6 +309,13 @@ class SchemaDocument(object):
             element.resolve()
         self._elm_instances = []
 
+    def register_import(self, namespace, schema):
+        schemas = self._imports.setdefault(namespace, [])
+        schemas.append(schema)
+
+    def is_imported(self, namespace):
+        return namespace in self._imports
+
     def register_type(self, name, value):
         assert not isinstance(value, type)
         assert value is not None
@@ -330,59 +351,36 @@ class SchemaDocument(object):
 
     def get_type(self, qname):
         """Return a xsd.Type object from this schema"""
-        try:
-            return self._types[qname]
-        except KeyError:
-            known_items = ', '.join(self._types.keys())
-            raise exceptions.LookupError((
-                "No type '%s' in namespace %s. " +
-                "Available types are: %s"
-            ) % (qname.localname, qname.namespace, known_items or ' - '))
+        return self._get_instance(qname, self._types, 'type')
 
     def get_element(self, qname):
         """Return a xsd.Element object from this schema"""
-        try:
-            return self._elements[qname]
-        except KeyError:
-            known_items = ', '.join(self._elements.keys())
-            raise exceptions.LookupError((
-                "No element '%s' in namespace %s. " +
-                "Available elements are: %s"
-            ) % (qname.localname, qname.namespace, known_items or ' - '))
+        return self._get_instance(qname, self._elements, 'element')
 
     def get_group(self, qname):
         """Return a xsd.Group object from this schema"""
-        try:
-            return self._groups[qname]
-        except KeyError:
-            known_items = ', '.join(self._groups.keys())
-            raise exceptions.LookupError((
-                "No group '%s' in namespace %s. " +
-                "Available attributes are: %s"
-            ) % (qname.localname, qname.namespace, known_items or ' - '))
+        return self._get_instance(qname, self._groups, 'group')
 
     def get_attribute(self, qname):
         """Return a xsd.Attribute object from this schema"""
-        try:
-            return self._attributes[qname]
-        except KeyError:
-            known_items = ', '.join(self._attributes.keys())
-            raise exceptions.LookupError((
-                "No attribute '%s' in namespace %s. " +
-                "Available attributes are: %s"
-            ) % (qname.localname, qname.namespace, known_items or ' - '))
+        return self._get_instance(qname, self._attributes, 'attribute')
 
     def get_attribute_group(self, qname):
         """Return a xsd.AttributeGroup object from this schema"""
+        return self._get_instance(qname, self._attribute_groups, 'attributeGroup')
+
+    def _get_instance(self, qname, items, item_name):
         try:
-            return self._attribute_groups[qname]
+            return items[qname]
         except KeyError:
-            known_items = ', '.join(self._attribute_groups.keys())
+            known_items = ', '.join(items.keys())
             raise exceptions.LookupError((
-                "No attributeGroup '%s' in namespace %s. " +
-                "Available attributeGroups are: %s"
-            ) % (qname.localname, qname.namespace, known_items or ' - '))
-
-    @property
-    def is_empty(self):
-        return not bool(self._imports or self._types or self._elements)
+                "No %(item_name)s '%(localname)s' in namespace %(namespace)s. " +
+                "Available %(item_name_plural)s are: %(known_items)s"
+            ) % {
+                'item_name': item_name,
+                'item_name_plural': item_name + 's',
+                'localname': qname.localname,
+                'namespace': qname.namespace,
+                'known_items': known_items or ' - '
+            })
diff --git a/src/zeep/xsd/types/__init__.py b/src/zeep/xsd/types/__init__.py
new file mode 100644
index 0000000..88ce9c9
--- /dev/null
+++ b/src/zeep/xsd/types/__init__.py
@@ -0,0 +1,5 @@
+from .any import *  # noqa
+from .base import *  # noqa
+from .collection import *  # noqa
+from .complex import *  # noqa
+from .simple import *  # noqa
diff --git a/src/zeep/xsd/types/any.py b/src/zeep/xsd/types/any.py
new file mode 100644
index 0000000..a667b61
--- /dev/null
+++ b/src/zeep/xsd/types/any.py
@@ -0,0 +1,74 @@
+import logging
+
+from zeep.utils import qname_attr
+from zeep.xsd.const import xsd_ns, xsi_ns
+from zeep.xsd.types.base import Type
+from zeep.xsd.valueobjects import AnyObject
+
+logger = logging.getLogger(__name__)
+
+__all__ = ['AnyType']
+
+
+class AnyType(Type):
+    _default_qname = xsd_ns('anyType')
+
+    def render(self, parent, value, xsd_type=None, render_path=None):
+        if isinstance(value, AnyObject):
+            if value.xsd_type is None:
+                parent.set(xsi_ns('nil'), 'true')
+            else:
+                value.xsd_type.render(parent, value.value, None, render_path)
+                parent.set(xsi_ns('type'), value.xsd_type.qname)
+        elif hasattr(value, '_xsd_elm'):
+            value._xsd_elm.render(parent, value, render_path)
+            parent.set(xsi_ns('type'), value._xsd_elm.qname)
+        else:
+            parent.text = self.xmlvalue(value)
+
+    def parse_xmlelement(self, xmlelement, schema=None, allow_none=True,
+                         context=None):
+        xsi_type = qname_attr(xmlelement, xsi_ns('type'))
+        xsi_nil = xmlelement.get(xsi_ns('nil'))
+        children = list(xmlelement.getchildren())
+
+        # Handle xsi:nil attribute
+        if xsi_nil == 'true':
+            return None
+
+        # Check if a xsi:type is defined and try to parse the xml according
+        # to that type.
+        if xsi_type and schema:
+            xsd_type = schema.get_type(xsi_type, fail_silently=True)
+
+            # If we were unable to resolve a type for the xsi:type (due to
+            # buggy soap servers) then we just return the lxml element.
+            if not xsd_type:
+                return children
+
+            # If the xsd_type is xsd:anyType then we will recurs so ignore
+            # that.
+            if isinstance(xsd_type, self.__class__):
+                return xmlelement.text or None
+
+            return xsd_type.parse_xmlelement(
+                xmlelement, schema, context=context)
+
+        # If no xsi:type is set and the element has children then there is
+        # not much we can do. Just return the children
+        elif children:
+            return children
+
+        elif xmlelement.text is not None:
+            return self.pythonvalue(xmlelement.text)
+
+        return None
+
+    def resolve(self):
+        return self
+
+    def xmlvalue(self, value):
+        return value
+
+    def pythonvalue(self, value, schema=None):
+        return value
diff --git a/src/zeep/xsd/types/base.py b/src/zeep/xsd/types/base.py
new file mode 100644
index 0000000..d67cb85
--- /dev/null
+++ b/src/zeep/xsd/types/base.py
@@ -0,0 +1,111 @@
+class Type(object):
+
+    def __init__(self, qname=None, is_global=False):
+        self.qname = qname
+        self.name = qname.localname if qname else None
+        self._resolved = False
+        self.is_global = is_global
+
+    def accept(self, value):
+        raise NotImplementedError
+
+    def validate(self, value, required=False):
+        return
+
+    def parse_kwargs(self, kwargs, name, available_kwargs):
+        value = None
+        name = name or self.name
+
+        if name in available_kwargs:
+            value = kwargs[name]
+            available_kwargs.remove(name)
+            return {name: value}
+        return {}
+
+    def parse_xmlelement(self, xmlelement, schema=None, allow_none=True,
+                         context=None):
+        raise NotImplementedError(
+            '%s.parse_xmlelement() is not implemented' % self.__class__.__name__)
+
+    def parsexml(self, xml, schema=None):
+        raise NotImplementedError
+
+    def render(self, parent, value, xsd_type=None, render_path=None):
+        raise NotImplementedError(
+            '%s.render() is not implemented' % self.__class__.__name__)
+
+    def resolve(self):
+        raise NotImplementedError(
+            '%s.resolve() is not implemented' % self.__class__.__name__)
+
+    def extend(self, child):
+        raise NotImplementedError(
+            '%s.extend() is not implemented' % self.__class__.__name__)
+
+    def restrict(self, child):
+        raise NotImplementedError(
+            '%s.restrict() is not implemented' % self.__class__.__name__)
+
+    @property
+    def attributes(self):
+        return []
+
+    @classmethod
+    def signature(cls, depth=()):
+        return ''
+
+
+class UnresolvedType(Type):
+    def __init__(self, qname, schema):
+        self.qname = qname
+        assert self.qname.text != 'None'
+        self.schema = schema
+
+    def __repr__(self):
+        return '<%s(qname=%r)>' % (self.__class__.__name__, self.qname)
+
+    def render(self, parent, value, xsd_type=None, render_path=None):
+        raise RuntimeError(
+            "Unable to render unresolved type %s. This is probably a bug." % (
+                self.qname))
+
+    def resolve(self):
+        retval = self.schema.get_type(self.qname)
+        return retval.resolve()
+
+
+class UnresolvedCustomType(Type):
+
+    def __init__(self, qname, base_type, schema):
+        assert qname is not None
+        self.qname = qname
+        self.name = str(qname.localname)
+        self.schema = schema
+        self.base_type = base_type
+
+    def __repr__(self):
+        return '<%s(qname=%r, base_type=%r)>' % (
+            self.__class__.__name__, self.qname.text, self.base_type)
+
+    def resolve(self):
+        base = self.base_type
+        base = base.resolve()
+
+        cls_attributes = {
+            '__module__': 'zeep.xsd.dynamic_types',
+        }
+
+        from zeep.xsd.types.collection import UnionType  # FIXME
+        from zeep.xsd.types.simple import AnySimpleType  # FIXME
+
+        if issubclass(base.__class__, UnionType):
+            xsd_type = type(self.name, (base.__class__,), cls_attributes)
+            return xsd_type(base.item_types)
+
+        elif issubclass(base.__class__, AnySimpleType):
+            xsd_type = type(self.name, (base.__class__,), cls_attributes)
+            return xsd_type(self.qname)
+
+        else:
+            xsd_type = type(self.name, (base.base_class,), cls_attributes)
+            return xsd_type(self.qname)
diff --git a/src/zeep/xsd/builtins.py b/src/zeep/xsd/types/builtins.py
similarity index 71%
rename from src/zeep/xsd/builtins.py
rename to src/zeep/xsd/types/builtins.py
index a4b64f3..49b8c3e 100644
--- a/src/zeep/xsd/builtins.py
+++ b/src/zeep/xsd/types/builtins.py
@@ -1,56 +1,3 @@
-"""
-    Primitive datatypes
-     - string
-     - boolean
-     - decimal
-     - float
-     - double
-     - duration
-     - dateTime
-     - time
-     - date
-     - gYearMonth
-     - gYear
-     - gMonthDay
-     - gDay
-     - gMonth
-     - hexBinary
-     - base64Binary
-     - anyURI
-     - QName
-     - NOTATION
-
-    Derived datatypes
-     - normalizedString
-     - token
-     - language
-     - NMTOKEN
-     - NMTOKENS
-     - Name
-     - NCName
-     - ID
-     - IDREF
-     - IDREFS
-     - ENTITY
-     - ENTITIES
-     - integer
-     - nonPositiveInteger
-     - negativeInteger
-     - long
-     - int
-     - short
-     - byte
-     - nonNegativeInteger
-     - unsignedLong
-     - unsignedInt
-     - unsignedShort
-     - unsignedByte
-     - positiveInteger
-
-
-"""
-from __future__ import division
-
 import base64
 import datetime
 import math
@@ -60,13 +7,10 @@ from decimal import Decimal as _Decimal
 import isodate
 import pytz
 import six
-from lxml import etree
 
-from zeep.utils import qname_attr
-from zeep.xsd.const import xsd_ns, xsi_ns, NS_XSD
-from zeep.xsd.elements import Base
-from zeep.xsd.types import SimpleType
-from zeep.xsd.valueobjects import AnyObject
+from zeep.xsd.const import xsd_ns
+from zeep.xsd.types.any import AnyType
+from zeep.xsd.types.simple import AnySimpleType
 
 
 class ParseError(ValueError):
@@ -84,33 +28,23 @@ def check_no_collection(func):
     return _wrapper
 
 
-class _BuiltinType(SimpleType):
-    def __init__(self, qname=None, is_global=False):
-        super(_BuiltinType, self).__init__(
-            qname or etree.QName(self._default_qname), is_global)
-
-    def signature(self, depth=()):
-        if self.qname.namespace == NS_XSD:
-            return 'xsd:%s' % self.name
-        return self.name
-
 ##
 # Primitive types
-
-
-class String(_BuiltinType):
+class String(AnySimpleType):
     _default_qname = xsd_ns('string')
     accepted_types = six.string_types
 
     @check_no_collection
     def xmlvalue(self, value):
+        if isinstance(value, bytes):
+            return value.decode('utf-8')
         return six.text_type(value if value is not None else '')
 
     def pythonvalue(self, value):
         return value
 
 
-class Boolean(_BuiltinType):
+class Boolean(AnySimpleType):
     _default_qname = xsd_ns('boolean')
     accepted_types = (bool,)
 
@@ -126,7 +60,7 @@ class Boolean(_BuiltinType):
         return value in ('true', '1')
 
 
-class Decimal(_BuiltinType):
+class Decimal(AnySimpleType):
     _default_qname = xsd_ns('decimal')
     accepted_types = (_Decimal, float) + six.string_types
 
@@ -138,7 +72,7 @@ class Decimal(_BuiltinType):
         return _Decimal(value)
 
 
-class Float(_BuiltinType):
+class Float(AnySimpleType):
     _default_qname = xsd_ns('float')
     accepted_types = (float, _Decimal) + six.string_types
 
@@ -149,7 +83,7 @@ class Float(_BuiltinType):
         return float(value)
 
 
-class Double(_BuiltinType):
+class Double(AnySimpleType):
     _default_qname = xsd_ns('double')
     accepted_types = (_Decimal, float) + six.string_types
 
@@ -161,7 +95,7 @@ class Double(_BuiltinType):
         return float(value)
 
 
-class Duration(_BuiltinType):
+class Duration(AnySimpleType):
     _default_qname = xsd_ns('duration')
     accepted_types = (isodate.duration.Duration,) + six.string_types
 
@@ -173,13 +107,23 @@ class Duration(_BuiltinType):
         return isodate.parse_duration(value)
 
 
-class DateTime(_BuiltinType):
+class DateTime(AnySimpleType):
     _default_qname = xsd_ns('dateTime')
     accepted_types = (datetime.datetime,) + six.string_types
 
     @check_no_collection
     def xmlvalue(self, value):
-        if value.microsecond:
+
+        # Bit of a hack, since datetime is a subclass of date we can't just
+        # test it with an isinstance(). And actually, we should not really
+        # care about the type, as long as it has the required attributes
+        if not all(hasattr(value, attr) for attr in ('hour', 'minute', 'second')):
+            value = datetime.datetime.combine(value, datetime.time(
+                getattr(value, 'hour', 0),
+                getattr(value, 'minute', 0),
+                getattr(value, 'second', 0)))
+
+        if getattr(value, 'microsecond', 0):
             return isodate.isostrf.strftime(value, '%Y-%m-%dT%H:%M:%S.%f%Z')
         return isodate.isostrf.strftime(value, '%Y-%m-%dT%H:%M:%S%Z')
 
@@ -187,7 +131,7 @@ class DateTime(_BuiltinType):
         return isodate.parse_datetime(value)
 
 
-class Time(_BuiltinType):
+class Time(AnySimpleType):
     _default_qname = xsd_ns('time')
     accepted_types = (datetime.time,) + six.string_types
 
@@ -201,7 +145,7 @@ class Time(_BuiltinType):
         return isodate.parse_time(value)
 
 
-class Date(_BuiltinType):
+class Date(AnySimpleType):
     _default_qname = xsd_ns('date')
     accepted_types = (datetime.date,) + six.string_types
 
@@ -215,7 +159,7 @@ class Date(_BuiltinType):
         return isodate.parse_date(value)
 
 
-class gYearMonth(_BuiltinType):
+class gYearMonth(AnySimpleType):
     """gYearMonth represents a specific gregorian month in a specific gregorian
     year.
 
@@ -242,7 +186,7 @@ class gYearMonth(_BuiltinType):
             _parse_timezone(group['timezone']))
 
 
-class gYear(_BuiltinType):
+class gYear(AnySimpleType):
     """gYear represents a gregorian calendar year.
 
     Lexical representation: CCYY
@@ -265,7 +209,7 @@ class gYear(_BuiltinType):
         return (int(group['year']), _parse_timezone(group['timezone']))
 
 
-class gMonthDay(_BuiltinType):
+class gMonthDay(AnySimpleType):
     """gMonthDay is a gregorian date that recurs, specifically a day of the
     year such as the third of May.
 
@@ -293,7 +237,7 @@ class gMonthDay(_BuiltinType):
             _parse_timezone(group['timezone']))
 
 
-class gDay(_BuiltinType):
+class gDay(AnySimpleType):
     """gDay is a gregorian day that recurs, specifically a day of the month
     such as the 5th of the month
 
@@ -317,7 +261,7 @@ class gDay(_BuiltinType):
         return (int(group['day']), _parse_timezone(group['timezone']))
 
 
-class gMonth(_BuiltinType):
+class gMonth(AnySimpleType):
     """gMonth is a gregorian month that recurs every year.
 
     Lexical representation: --MM
@@ -340,7 +284,7 @@ class gMonth(_BuiltinType):
         return (int(group['month']), _parse_timezone(group['timezone']))
 
 
-class HexBinary(_BuiltinType):
+class HexBinary(AnySimpleType):
     accepted_types = six.string_types
     _default_qname = xsd_ns('hexBinary')
 
@@ -352,7 +296,7 @@ class HexBinary(_BuiltinType):
         return value
 
 
-class Base64Binary(_BuiltinType):
+class Base64Binary(AnySimpleType):
     accepted_types = six.string_types
     _default_qname = xsd_ns('base64Binary')
 
@@ -364,7 +308,7 @@ class Base64Binary(_BuiltinType):
         return base64.b64decode(value)
 
 
-class AnyURI(_BuiltinType):
+class AnyURI(AnySimpleType):
     accepted_types = six.string_types
     _default_qname = xsd_ns('anyURI')
 
@@ -376,7 +320,7 @@ class AnyURI(_BuiltinType):
         return value
 
 
-class QName(_BuiltinType):
+class QName(AnySimpleType):
     accepted_types = six.string_types
     _default_qname = xsd_ns('QName')
 
@@ -388,7 +332,7 @@ class QName(_BuiltinType):
         return value
 
 
-class Notation(_BuiltinType):
+class Notation(AnySimpleType):
     accepted_types = six.string_types
     _default_qname = xsd_ns('NOTATION')
 
@@ -508,61 +452,6 @@ class PositiveInteger(NonNegativeInteger):
 
 ##
 # Other
-
-class AnyType(_BuiltinType):
-    _default_qname = xsd_ns('anyType')
-
-    def render(self, parent, value):
-        if isinstance(value, AnyObject):
-            value.xsd_type.render(parent, value.value)
-            parent.set(xsi_ns('type'), value.xsd_type.qname)
-        elif hasattr(value, '_xsd_elm'):
-            value._xsd_elm.render(parent, value)
-            parent.set(xsi_ns('type'), value._xsd_elm.qname)
-        else:
-            parent.text = self.xmlvalue(value)
-
-    def parse_xmlelement(self, xmlelement, schema=None, allow_none=True,
-                         context=None):
-        xsi_type = qname_attr(xmlelement, xsi_ns('type'))
-        xsi_nil = xmlelement.get(xsi_ns('nil'))
-
-        # Handle xsi:nil attribute
-        if xsi_nil == "true":
-            return None
-
-        if xsi_type and schema:
-            xsd_type = schema.get_type(xsi_type, fail_silently=True)
-
-            # If we were unable to resolve a type for the xsi:type (due to
-            # buggy soap servers) then we just return the lxml element.
-            if not xsd_type:
-                return xmlelement.getchildren()
-
-            # If the xsd_type is xsd:anyType then we will recurs so ignore
-            # that.
-            if isinstance(xsd_type, self.__class__):
-                return xmlelement.text or None
-
-            return xsd_type.parse_xmlelement(
-                xmlelement, schema, context=context)
-
-        if xmlelement.text is None:
-            return
-
-        return self.pythonvalue(xmlelement.text)
-
-    def xmlvalue(self, value):
-        return value
-
-    def pythonvalue(self, value, schema=None):
-        return value
-
-
-class AnySimpleType(AnyType):
-    _default_qname = xsd_ns('anySimpleType')
-
-
 def _parse_timezone(val):
     """Return a pytz.tzinfo object"""
     if not val:
@@ -650,39 +539,3 @@ default_types = {
         AnySimpleType,
     ]
 }
-
-
-class Schema(Base):
-    name = 'schema'
-    attr_name = 'schema'
-    qname = xsd_ns('schema')
-
-    def clone(self, qname, min_occurs=1, max_occurs=1):
-        return self.__class__()
-
-    def parse_kwargs(self, kwargs, name, available_kwargs):
-        if name in available_kwargs:
-            value = kwargs[name]
-            available_kwargs.remove(name)
-            return {name: value}
-        return {}
-
-    def parse(self, xmlelement, schema, context=None):
-        from zeep.xsd.schema import Schema
-        schema = Schema(xmlelement, schema._transport)
-        context.schemas.append(schema)
-        return schema
-
-    def parse_xmlelements(self, xmlelements, schema, name=None, context=None):
-        if xmlelements[0].tag == self.qname:
-            xmlelement = xmlelements.popleft()
-            result = self.parse(xmlelement, schema, context=context)
-            return result
-
-    def resolve(self):
-        return self
-
-
-default_elements = {
-    xsd_ns('schema'): Schema(),
-}
diff --git a/src/zeep/xsd/types/collection.py b/src/zeep/xsd/types/collection.py
new file mode 100644
index 0000000..f18b14c
--- /dev/null
+++ b/src/zeep/xsd/types/collection.py
@@ -0,0 +1,73 @@
+from zeep.utils import get_base_class
+from zeep.xsd.types.simple import AnySimpleType
+
+__all__ = ['ListType', 'UnionType']
+
+
+class ListType(AnySimpleType):
+    """Space separated list of simpleType values"""
+
+    def __init__(self, item_type):
+        self.item_type = item_type
+        super(ListType, self).__init__()
+
+    def __call__(self, value):
+        return value
+
+    def render(self, parent, value, xsd_type=None, render_path=None):
+        parent.text = self.xmlvalue(value)
+
+    def resolve(self):
+        self.item_type = self.item_type.resolve()
+        self.base_class = self.item_type.__class__
+        return self
+
+    def xmlvalue(self, value):
+        item_type = self.item_type
+        return ' '.join(item_type.xmlvalue(v) for v in value)
+
+    def pythonvalue(self, value):
+        if not value:
+            return []
+        item_type = self.item_type
+        return [item_type.pythonvalue(v) for v in value.split()]
+
+    def signature(self, depth=()):
+        return self.item_type.signature(depth) + '[]'
+
+
+class UnionType(AnySimpleType):
+    """Simple type existing out of multiple other types"""
+
+    def __init__(self, item_types):
+        self.item_types = item_types
+        self.item_class = None
+        assert item_types
+        super(UnionType, self).__init__(None)
+
+    def resolve(self):
+        self.item_types = [item.resolve() for item in self.item_types]
+        base_class = get_base_class(self.item_types)
+        if issubclass(base_class, AnySimpleType) and base_class != AnySimpleType:
+            self.item_class = base_class
+        return self
+
+    def signature(self, depth=()):
+        return ''
+
+    def parse_xmlelement(self, xmlelement, schema=None, allow_none=True,
+                         context=None):
+        if self.item_class:
+            return self.item_class().parse_xmlelement(
+                xmlelement, schema, allow_none, context)
+        return xmlelement.text
+
+    def pythonvalue(self, value):
+        if self.item_class:
+            return self.item_class().pythonvalue(value)
+        return value
+
+    def xmlvalue(self, value):
+        if self.item_class:
+            return self.item_class().xmlvalue(value)
+        return value
diff --git a/src/zeep/xsd/types.py b/src/zeep/xsd/types/complex.py
similarity index 59%
rename from src/zeep/xsd/types.py
rename to src/zeep/xsd/types/complex.py
index 87901b1..c1b6925 100644
--- a/src/zeep/xsd/types.py
+++ b/src/zeep/xsd/types/complex.py
@@ -3,194 +3,24 @@ import logging
 from collections import OrderedDict, deque
 from itertools import chain
 
-import six
 from cached_property import threaded_cached_property
 
-from zeep.exceptions import XMLParseError, UnexpectedElementError
-from zeep.xsd.const import xsi_ns
-from zeep.xsd.elements import Any, AnyAttribute, AttributeGroup, Element
-from zeep.xsd.indicators import Group, OrderIndicator, Sequence
+from zeep.exceptions import UnexpectedElementError, XMLParseError
+from zeep.xsd.const import xsi_ns, SkipValue, NotSet
+from zeep.xsd.elements import (
+    Any, AnyAttribute, AttributeGroup, Choice, Element, Group, Sequence)
+from zeep.xsd.elements.indicators import OrderIndicator
+from zeep.xsd.types.any import AnyType
+from zeep.xsd.types.simple import AnySimpleType
 from zeep.xsd.utils import NamePrefixGenerator
-from zeep.utils import get_base_class
 from zeep.xsd.valueobjects import CompoundValue
 
-
 logger = logging.getLogger(__name__)
 
+__all__ = ['ComplexType']
 
-class Type(object):
-
-    def __init__(self, qname=None, is_global=False):
-        self.qname = qname
-        self.name = qname.localname if qname else None
-        self._resolved = False
-        self.is_global = is_global
-
-    def accept(self, value):
-        raise NotImplementedError
-
-    def parse_kwargs(self, kwargs, name, available_kwargs):
-        value = None
-        name = name or self.name
-
-        if name in available_kwargs:
-            value = kwargs[name]
-            available_kwargs.remove(name)
-            return {name: value}
-        return {}
-
-    def parse_xmlelement(self, xmlelement, schema=None, allow_none=True,
-                         context=None):
-        raise NotImplementedError(
-            '%s.parse_xmlelement() is not implemented' % self.__class__.__name__)
-
-    def parsexml(self, xml, schema=None):
-        raise NotImplementedError
-
-    def render(self, parent, value):
-        raise NotImplementedError(
-            '%s.render() is not implemented' % self.__class__.__name__)
-
-    def resolve(self):
-        raise NotImplementedError(
-            '%s.resolve() is not implemented' % self.__class__.__name__)
-
-    def extend(self, child):
-        raise NotImplementedError(
-            '%s.extend() is not implemented' % self.__class__.__name__)
-
-    def restrict(self, child):
-        raise NotImplementedError(
-            '%s.restrict() is not implemented' % self.__class__.__name__)
-
-    @property
-    def attributes(self):
-        return []
-
-    @classmethod
-    def signature(cls, depth=()):
-        return ''
-
-
-class UnresolvedType(Type):
-    def __init__(self, qname, schema):
-        self.qname = qname
-        assert self.qname.text != 'None'
-        self.schema = schema
-
-    def __repr__(self):
-        return '<%s(qname=%r)>' % (self.__class__.__name__, self.qname)
-
-    def render(self, parent, value):
-        raise RuntimeError(
-            "Unable to render unresolved type %s. This is probably a bug." % (
-                self.qname))
-
-    def resolve(self):
-        retval = self.schema.get_type(self.qname)
-        return retval.resolve()
-
-
-class UnresolvedCustomType(Type):
-
-    def __init__(self, qname, base_type, schema):
-        assert qname is not None
-        self.qname = qname
-        self.name = str(qname.localname)
-        self.schema = schema
-        self.base_type = base_type
-
-    def __repr__(self):
-        return '<%s(qname=%r, base_type=%r)>' % (
-            self.__class__.__name__, self.qname.text, self.base_type)
-
-    def resolve(self):
-        base = self.base_type
-        base = base.resolve()
-
-        cls_attributes = {
-            '__module__': 'zeep.xsd.dynamic_types',
-        }
-
-        if issubclass(base.__class__, UnionType):
-            xsd_type = type(self.name, (base.__class__,), cls_attributes)
-            return xsd_type(base.item_types)
-
-        elif issubclass(base.__class__, SimpleType):
-            xsd_type = type(self.name, (base.__class__,), cls_attributes)
-            return xsd_type(self.qname)
-
-        else:
-            xsd_type = type(self.name, (base.base_class,), cls_attributes)
-            return xsd_type(self.qname)
-
-
- at six.python_2_unicode_compatible
-class SimpleType(Type):
-    accepted_types = six.string_types
-
-    def __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'
-
-        """
-        num_args = len(args) + len(kwargs)
-        if num_args != 1:
-            raise TypeError((
-                '%s() takes exactly 1 argument (%d given). ' +
-                'Simple types expect only a single value argument'
-            ) % (self.__class__.__name__, num_args))
-
-        if kwargs and 'value' not in kwargs:
-            raise TypeError((
-                '%s() got an unexpected keyword argument %r. ' +
-                'Simple types expect only a single value argument'
-            ) % (self.__class__.__name__, next(six.iterkeys(kwargs))))
-
-        value = args[0] if args else kwargs['value']
-        return self.xmlvalue(value)
-
-    def __eq__(self, other):
-        return (
-            other is not None and
-            self.__class__ == other.__class__ and
-            self.__dict__ == other.__dict__)
-
-    def __str__(self):
-        return '%s(value)' % (self.__class__.__name__)
-
-    def parse_xmlelement(self, xmlelement, schema=None, allow_none=True,
-                         context=None):
-        if xmlelement.text is None:
-            return
-        try:
-            return self.pythonvalue(xmlelement.text)
-        except (TypeError, ValueError):
-            logger.exception("Error during xml -> python translation")
-            return None
 
-    def pythonvalue(self, xmlvalue):
-        raise NotImplementedError(
-            '%s.pytonvalue() not implemented' % self.__class__.__name__)
-
-    def render(self, parent, value):
-        parent.text = self.xmlvalue(value)
-
-    def resolve(self):
-        return self
-
-    def signature(self, depth=()):
-        return self.name
-
-    def xmlvalue(self, value):
-        raise NotImplementedError(
-            '%s.xmlvalue() not implemented' % self.__class__.__name__)
-
-
-class ComplexType(Type):
+class ComplexType(AnyType):
     _xsd_name = None
 
     def __init__(self, element=None, attributes=None,
@@ -293,7 +123,7 @@ class ComplexType(Type):
         # If this complexType extends a simpleType then we have no nested
         # elements. Parse it directly via the type object. This is the case
         # for xsd:simpleContent
-        if isinstance(self._element, Element) and isinstance(self._element.type, SimpleType):
+        if isinstance(self._element, Element) and isinstance(self._element.type, AnySimpleType):
             name, element = self.elements_nested[0]
             init_kwargs[name] = element.type.parse_xmlelement(
                 xmlelement, schema, name, context=context)
@@ -330,30 +160,39 @@ class ComplexType(Type):
 
         return self(**init_kwargs)
 
-    def render(self, parent, value, xsd_type=None):
+    def render(self, parent, value, xsd_type=None, render_path=None):
         """Serialize the given value lxml.Element subelements on the parent
         element.
 
         """
+        if not render_path:
+            render_path = [self.name]
+
         if not self.elements_nested and not self.attributes:
             return
 
         # Render attributes
         for name, attribute in self.attributes:
-            attr_value = getattr(value, name, None)
-            attribute.render(parent, attr_value)
+            attr_value = value[name] if name in value else NotSet
+            child_path = render_path + [name]
+            attribute.render(parent, attr_value, child_path)
 
         # Render sub elements
         for name, element in self.elements_nested:
             if isinstance(element, Element) or element.accepts_multiple:
-                element_value = getattr(value, name, None)
+                element_value = value[name] if name in value else NotSet
+                child_path = render_path + [name]
             else:
                 element_value = value
+                child_path = list(render_path)
+
+            if element_value is SkipValue:
+                continue
 
             if isinstance(element, Element):
-                element.type.render(parent, element_value)
+                element.type.render(parent, element_value, None, child_path)
             else:
-                element.render(parent, element_value)
+                element.render(parent, element_value, child_path)
 
         if xsd_type:
             if xsd_type._xsd_name:
@@ -435,6 +274,9 @@ class ComplexType(Type):
 
         Used for handling xsd:extension tags
 
+        TODO: Needs a rewrite where the child containers are responsible for
+        the extend functionality.
+
         """
         if isinstance(base, ComplexType):
             base_attributes = base._attributes_unwrapped
@@ -460,9 +302,13 @@ class ComplexType(Type):
         element = []
         if self._element and base_element:
             element = self._element.clone(self._element.name)
-            if isinstance(element, OrderIndicator) and isinstance(base_element, OrderIndicator):
-                for item in reversed(base_element):
-                    element.insert(0, item)
+            if isinstance(base_element, OrderIndicator):
+                if isinstance(self._element, Choice):
+                    element = base_element.clone(self._element.name)
+                    element.append(self._element)
+                elif isinstance(element, OrderIndicator):
+                    for item in reversed(base_element):
+                        element.insert(0, item)
 
             elif isinstance(self._element, Group):
                 raise NotImplementedError('TODO')
@@ -528,73 +374,3 @@ class ComplexType(Type):
         if len(depth) > 1:
             value = '{%s}' % value
         return value
-
-
-class ListType(SimpleType):
-    """Space separated list of simpleType values"""
-
-    def __init__(self, item_type):
-        self.item_type = item_type
-        super(ListType, self).__init__()
-
-    def __call__(self, value):
-        return value
-
-    def render(self, parent, value):
-        parent.text = self.xmlvalue(value)
-
-    def resolve(self):
-        self.item_type = self.item_type.resolve()
-        self.base_class = self.item_type.__class__
-        return self
-
-    def xmlvalue(self, value):
-        item_type = self.item_type
-        return ' '.join(item_type.xmlvalue(v) for v in value)
-
-    def pythonvalue(self, value):
-        if not value:
-            return []
-        item_type = self.item_type
-        return [item_type.pythonvalue(v) for v in value.split()]
-
-    def signature(self, depth=()):
-        return self.item_type.signature(depth) + '[]'
-
-
-class UnionType(SimpleType):
-
-    def __init__(self, item_types):
-        self.item_types = item_types
-        self.item_class = None
-        assert item_types
-        super(UnionType, self).__init__(None)
-
-    def resolve(self):
-        from zeep.xsd.builtins import _BuiltinType
-
-        self.item_types = [item.resolve() for item in self.item_types]
-        base_class = get_base_class(self.item_types)
-        if issubclass(base_class, _BuiltinType) and base_class != _BuiltinType:
-            self.item_class = base_class
-        return self
-
-    def signature(self, depth=()):
-        return ''
-
-    def parse_xmlelement(self, xmlelement, schema=None, allow_none=True,
-                         context=None):
-        if self.item_class:
-            return self.item_class().parse_xmlelement(
-                xmlelement, schema, allow_none, context)
-        return xmlelement.text
-
-    def pythonvalue(self, value):
-        if self.item_class:
-            return self.item_class().pythonvalue(value)
-        return value
-
-    def xmlvalue(self, value):
-        if self.item_class:
-            return self.item_class().xmlvalue(value)
-        return value
diff --git a/src/zeep/xsd/types/simple.py b/src/zeep/xsd/types/simple.py
new file mode 100644
index 0000000..989d292
--- /dev/null
+++ b/src/zeep/xsd/types/simple.py
@@ -0,0 +1,84 @@
+import logging
+
+import six
+from lxml import etree
+
+from zeep.exceptions import ValidationError
+from zeep.xsd.const import NS_XSD, xsd_ns
+from zeep.xsd.types.any import AnyType
+
+logger = logging.getLogger(__name__)
+
+__all__ = ['AnySimpleType']
+
+
+ at six.python_2_unicode_compatible
+class AnySimpleType(AnyType):
+    _default_qname = xsd_ns('anySimpleType')
+
+    def __init__(self, qname=None, is_global=False):
+        super(AnySimpleType, self).__init__(
+            qname or etree.QName(self._default_qname), is_global)
+
+    def __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'
+
+        """
+        num_args = len(args) + len(kwargs)
+        if num_args != 1:
+            raise TypeError((
+                '%s() takes exactly 1 argument (%d given). ' +
+                'Simple types expect only a single value argument'
+            ) % (self.__class__.__name__, num_args))
+
+        if kwargs and 'value' not in kwargs:
+            raise TypeError((
+                '%s() got an unexpected keyword argument %r. ' +
+                'Simple types expect only a single value argument'
+            ) % (self.__class__.__name__, next(six.iterkeys(kwargs))))
+
+        value = args[0] if args else kwargs['value']
+        return self.xmlvalue(value)
+
+    def __eq__(self, other):
+        return (
+            other is not None and
+            self.__class__ == other.__class__ and
+            self.__dict__ == other.__dict__)
+
+    def __str__(self):
+        return '%s(value)' % (self.__class__.__name__)
+
+    def parse_xmlelement(self, xmlelement, schema=None, allow_none=True,
+                         context=None):
+        if xmlelement.text is None:
+            return
+        try:
+            return self.pythonvalue(xmlelement.text)
+        except (TypeError, ValueError):
+            logger.exception("Error during xml -> python translation")
+            return None
+
+    def pythonvalue(self, xmlvalue):
+        raise NotImplementedError(
+            '%s.pytonvalue() not implemented' % self.__class__.__name__)
+
+    def render(self, parent, value, xsd_type=None, render_path=None):
+        parent.text = self.xmlvalue(value)
+
+    def signature(self, depth=()):
+        if self.qname.namespace == NS_XSD:
+            return 'xsd:%s' % self.name
+        return self.name
+
+    def validate(self, value, required=False):
+        if required and value is None:
+            raise ValidationError("Value is required")
+
+    def xmlvalue(self, value):
+        raise NotImplementedError(
+            '%s.xmlvalue() not implemented' % self.__class__.__name__)
diff --git a/src/zeep/xsd/utils.py b/src/zeep/xsd/utils.py
index 108cfe8..2b5a3cc 100644
--- a/src/zeep/xsd/utils.py
+++ b/src/zeep/xsd/utils.py
@@ -1,4 +1,10 @@
+from defusedxml.lxml import fromstring
+from lxml import etree
+
 from six.moves import range
+from six.moves.urllib.parse import urlparse
+from zeep.exceptions import XMLSyntaxError
+from zeep.parser import absolute_location
 
 
 class NamePrefixGenerator(object):
@@ -25,9 +31,41 @@ class UniqueNameGenerator(object):
             return name
 
 
-def max_occurs_iter(max_occurs):
+class ImportResolver(etree.Resolver):
+    """Custom lxml resolve to use the transport object"""
+    def __init__(self, transport):
+        self.transport = transport
+
+    def resolve(self, url, pubid, context):
+        if urlparse(url).scheme in ('http', 'https'):
+            content = self.transport.load(url)
+            return self.resolve_string(content, context)
+
+
+def parse_xml(content, transport, base_url=None):
+    parser = etree.XMLParser(remove_comments=True, resolve_entities=False)
+    parser.resolvers.add(ImportResolver(transport))
+    try:
+        return fromstring(content, parser=parser, base_url=base_url)
+    except etree.XMLSyntaxError as exc:
+        raise XMLSyntaxError("Invalid XML content received (%s)" % exc.message)
+
+
+def load_external(url, transport, base_url=None):
+    if base_url:
+        url = absolute_location(url, base_url)
+
+    response = transport.load(url)
+    return parse_xml(response, transport, base_url)
+
+
+def max_occurs_iter(max_occurs, items=None):
     assert max_occurs is not None
-    if max_occurs == 'unbounded':
-        return range(0, 2**31-1)
+    generator = range(0, max_occurs if max_occurs != 'unbounded' else 2**31-1)
+
+    if items is not None:
+        for i, sub_kwargs in zip(generator, items):
+            yield sub_kwargs
     else:
-        return range(max_occurs)
+        for i in generator:
+            yield i
diff --git a/src/zeep/xsd/valueobjects.py b/src/zeep/xsd/valueobjects.py
index 8cd5f18..c2cf9c5 100644
--- a/src/zeep/xsd/valueobjects.py
+++ b/src/zeep/xsd/valueobjects.py
@@ -1,14 +1,18 @@
 import copy
 from collections import OrderedDict
 
-import six
-
 from zeep.xsd.printer import PrettyPrinter
 
 __all__ = ['AnyObject', 'CompoundValue']
 
 
 class AnyObject(object):
+    """Create an any object
+
+    :param xsd_object: the xsd type
+    :param value: The value
+
+    """
     def __init__(self, xsd_object, value):
         self.xsd_obj = xsd_object
         self.value = value
diff --git a/src/zeep/xsd/visitor.py b/src/zeep/xsd/visitor.py
index 6a77340..8ed0160 100644
--- a/src/zeep/xsd/visitor.py
+++ b/src/zeep/xsd/visitor.py
@@ -1,20 +1,17 @@
 import keyword
 import logging
 import re
-import warnings
 
 from lxml import etree
 
 from zeep import exceptions
-from zeep.exceptions import XMLParseError, ZeepWarning
+from zeep.exceptions import XMLParseError
 from zeep.parser import absolute_location
 from zeep.utils import as_qname, qname_attr
-from zeep.xsd import builtins as xsd_builtins
 from zeep.xsd import elements as xsd_elements
-from zeep.xsd import indicators as xsd_indicators
 from zeep.xsd import types as xsd_types
 from zeep.xsd.const import xsd_ns
-from zeep.xsd.parser import load_external
+from zeep.xsd.utils import load_external
 
 logger = logging.getLogger(__name__)
 
@@ -41,10 +38,9 @@ class SchemaVisitor(object):
     types in the given schema.
 
     """
-    def __init__(self, document, parser_context=None):
+    def __init__(self, schema, document):
         self.document = document
-        self.schema = document._schema
-        self.parser_context = parser_context
+        self.schema = schema
         self._includes = set()
 
     def process(self, node, parent):
@@ -131,25 +127,17 @@ class SchemaVisitor(object):
         if not namespace and not self.document._target_namespace:
             raise XMLParseError(
                 "The attribute 'namespace' must be existent if the "
-                "importing schema has no target namespace.")
+                "importing schema has no target namespace.",
+                filename=self._document.location,
+                sourceline=node.sourceline)
 
         # Check if the schema is already imported before based on the
         # namespace. Schema's without namespace are registered as 'None'
-        schema = self.parser_context.schema_objects.get(namespace)
-        if schema:
-            if location and schema._location != location:
-                # Use same warning message as libxml2
-                message = (
-                    "Skipping import of schema located at %r " +
-                    "for the namespace %r, since the namespace was " +
-                    "already imported with the schema located at %r"
-                    ) % (location, namespace or '(null)', schema._location)
-                warnings.warn(message, ZeepWarning, stacklevel=6)
-
-                return
+        document = self.schema._get_schema_document(namespace, location)
+        if document:
             logger.debug("Returning existing schema: %r", location)
-            self.document._imports[namespace] = schema
-            return schema
+            self.document.register_import(namespace, document)
+            return document
 
         # Hardcode the mapping between the xml namespace and the xsd for now.
         # This seems to fix issues with exchange wsdl's, see #220
@@ -165,8 +153,7 @@ class SchemaVisitor(object):
             return
 
         # Load the XML
-        schema_node = load_external(
-            location, self.document._transport, self.parser_context)
+        schema_node = load_external(location, self.schema._transport)
 
         # Check if the xsd:import namespace matches the targetNamespace. If
         # the xsd:import statement didn't specify a namespace then make sure
@@ -176,28 +163,12 @@ class SchemaVisitor(object):
             raise XMLParseError((
                 "The namespace defined on the xsd:import doesn't match the "
                 "imported targetNamespace located at %r "
-                ) % (location))
-        elif schema_tns in self.parser_context.schema_objects:
-            schema = self.parser_context.schema_objects.get(schema_tns)
-            message = (
-                "Skipping import of schema located at %r " +
-                "for the namespace %r, since the namespace was " +
-                "already imported with the schema located at %r"
-                ) % (location, namespace or '(null)', schema._location)
-            warnings.warn(message, ZeepWarning, stacklevel=6)
-
-        # If this schema location is 'internal' then retrieve the original
-        # location since that is used as base url for sub include/imports
-        if location in self.parser_context.schema_locations:
-            base_url = self.parser_context.schema_locations[location]
-        else:
-            base_url = location
-
-        schema = self.document.__class__(
-            schema_node, self.document._transport, self.schema, location,
-            self.parser_context, base_url)
+                ) % (location),
+                filename=self.document._location,
+                sourceline=node.sourceline)
 
-        self.document._imports[namespace] = schema
+        schema = self.schema.create_new_document(schema_node, location)
+        self.document.register_import(namespace, schema)
         return schema
 
     def visit_include(self, node, parent):
@@ -217,8 +188,7 @@ class SchemaVisitor(object):
             return
 
         schema_node = load_external(
-            location, self.document._transport, self.parser_context,
-            base_url=self.document._base_url)
+            location, self.schema._transport, base_url=self.document._base_url)
         self._includes.add(location)
 
         return self.visit_schema(schema_node)
@@ -288,7 +258,7 @@ class SchemaVisitor(object):
             if node_type:
                 xsd_type = self._get_type(node_type.text)
             else:
-                xsd_type = xsd_builtins.AnyType()
+                xsd_type = xsd_types.AnyType()
 
         # Naive workaround to mark fields which are part of a choice element
         # as optional
@@ -360,7 +330,7 @@ class SchemaVisitor(object):
             if node_type:
                 xsd_type = self._get_type(node_type)
             else:
-                xsd_type = xsd_builtins.AnyType()
+                xsd_type = xsd_types.AnyType()
 
         # TODO: We ignore 'prohobited' for now
         required = node.get('use') == 'required'
@@ -690,7 +660,7 @@ class SchemaVisitor(object):
             tags.group, tags.sequence
         ]
         min_occurs, max_occurs = _process_occurs_attrs(node)
-        result = xsd_indicators.Sequence(
+        result = xsd_elements.Sequence(
             min_occurs=min_occurs, max_occurs=max_occurs)
 
         annotation, items = self._pop_annotation(node.getchildren())
@@ -719,7 +689,7 @@ class SchemaVisitor(object):
         sub_types = [
             tags.annotation, tags.element
         ]
-        result = xsd_indicators.All()
+        result = xsd_elements.All()
 
         for child in node.iterchildren():
             assert child.tag in sub_types, child
@@ -757,7 +727,7 @@ class SchemaVisitor(object):
         child = children[0]
 
         item = self.process(child, parent)
-        elm = xsd_indicators.Group(name=qname, child=item)
+        elm = xsd_elements.Group(name=qname, child=item)
 
         if parent.tag == tags.schema:
             self.document.register_group(qname, elm)
@@ -804,7 +774,7 @@ class SchemaVisitor(object):
         for child in children:
             elm = self.process(child, node)
             choices.append(elm)
-        return xsd_indicators.Choice(
+        return xsd_elements.Choice(
             choices, min_occurs=min_occurs, max_occurs=max_occurs)
 
     def visit_union(self, node, parent):
@@ -922,7 +892,7 @@ class SchemaVisitor(object):
         # referenced.
         if (
             name.namespace == 'http://schemas.xmlsoap.org/soap/encoding/' and
-            name.namespace not in self.document._imports
+            not self.document.is_imported(name.namespace)
         ):
             import_node = etree.Element(
                 tags.import_,
@@ -943,11 +913,14 @@ class SchemaVisitor(object):
     def _process_attributes(self, node, items):
         attributes = []
         for child in items:
-            attribute = self.process(child, node)
             if child.tag in (tags.attribute, tags.attributeGroup, tags.anyAttribute):
+                attribute = self.process(child, node)
                 attributes.append(attribute)
             else:
-                raise XMLParseError("Unexpected tag: %s" % child.tag)
+                raise XMLParseError(
+                    "Unexpected tag `%s`" % (child.tag),
+                    filename=self.document._location,
+                    sourceline=node.sourceline)
         return attributes
 
     visitors = {
diff --git a/tests/cert_valid.pem b/tests/cert_valid.pem
new file mode 100644
index 0000000..daad5f5
--- /dev/null
+++ b/tests/cert_valid.pem
@@ -0,0 +1,54 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIEpAIBAAKCAQEAyrWxlRtnAT7q2IgWEaQUCwG+0gNuqTNPUi3fLZ/7XI7lOBem
+TT3YeCZc7fbTLrABH8l4NEdXG58CFa/Mkdsm05Xt4G3avrYT+CNZA3tOf42TsGIB
+5/Vvqd9LS4uhBUo2qqcTUz1VA8jP7CKY25dq8RLEd1gw3lLjPGqYa69sUkEYzABC
+uJfP9xUEySLeclxWx+ky2Pp8AAXPpDtlGdb9rszxYhSJtce/KEsbZqaCQ9a2ZAJe
+FauJiUvODPNigs8vUQLGNYHcmEm4SvVkSSgvpKekAk1gC51OqLYypp8c916n1DG2
+PC5b9kTj6mUXkBoa9jjWJQYkE8zG5wbaM7xAGwIDAQABAoIBAQCPlGqYRrSK+Vm2
+zY10KVfZA/95Gd1EE4fXmY4+7tZIFR7ewEGW7HtrfyDLnMywgRIKxdVSkkVs1w/O
+9JpdpXC25bd8A9OwyZ8TX1YpVSmgx1MY2BKpjfrtw6+9bsU6zfoynezeRM72w0Ii
+686Bm5qv7q8iKWFT2DoEDSyw+awsBZQokVTCwHFWdbXZ50mAXoXxovn19DTRNqzD
+yqO8dae9gjk16vap7gRpB60Y/YZ4Rf46X47SlRqTcqgEB/C/1jyGtl3jQlaLq4KL
+POe1jFZYGUZTctmRvsol4VdSzfITqr/kd3DhJw0LxvXnT6c02wxzKLCSo2HnN6HT
+A7l6eEWhAoGBAPZ46R8qPln9uGhbdYjtEjR+vxDhQcuCNkjn40psEOyXu62cFhFO
+FSj3lVCyRCHIhrUWUzJIQTIPDnczH7iwrWZlqUujjYKs3DJcpu7J5B4ODatklXO+
+2NZa45XEto6ygOPUp7OYZhLlGpjWnC2yp0XLqAEC0URkc1zOTTfJ0VFNAoGBANKL
+tXPJLOZ2F1e3IPkX6y1hfbfbRlyuA2vai/2cAhbld4oZIpm7Yy6Jw4BFuDaUs02P
+nDGBBh6EVgbZNZphZEUhgvglSdJaa2/3cS+1pGcnjmYMj4xywHpOxiomgZ8Xa1LW
+ZuJdD2SajS0yPYcrEDg+xBQBvDpE0NEIka6Zu6MHAoGBAMVbKegPjl/GvuupGGMs
+2Z/5QYsFpAaN3GPicmh8Qc0A7oHkcvMmX+Eu5nv4Un/urpbAKpwfqTypO78My8C6
+kA5nJvlvG/ff7G3TLMQWGzhJrn5oCxfkYIK7wnKBUmDO5FAKTsKLLGjC1No/Nk2N
+OU209nDgzaqC+LD+bGxYiOgdAoGAWFtXD7s6Q5EFZMMubDqUcFv8dV7pHVXNi8KQ
+gyKoYdF0pBi+Q4O3ML2RtNANaaJnyMHey4uY9M+WhpM7AomimbxhiR+k5kkZ00gl
+UN9Kmhuoj7zvtQInMmzCjsfQF+KtIHtne9GP9ylA29m8pm/1A5WblcXQpydf9olB
+EePkMZsCgYABr07cGT31CXxrbQwDTgiQJm2JHq/wIR+q0eys1aiMvKRN+0arfqvz
+j8zPK6B9SRcCXY4XAda3rilsF/7eHf2zkg/0kHV6NqaSWFEA8yqAoIqpc03cE/ef
+lUgGakZ6Wb0sucIRB40loAZIu0lN0kF45K1P8JDHg74jk6uM2xnZvg==
+-----END RSA PRIVATE KEY-----
+-----BEGIN CERTIFICATE-----
+MIIEqjCCA5KgAwIBAgIJALOCBen0S+W5MA0GCSqGSIb3DQEBBQUAMIGUMQswCQYD
+VQQGEwJOTDEQMA4GA1UECBMHVXRyZWNodDEQMA4GA1UEBxMHVXRyZWNodDEeMBwG
+A1UEChMVTWljaGFlbCB2YW4gVGVsbGluZ2VuMRwwGgYDVQQDExN3d3cucHl0aG9u
+LXplZXAub3JnMSMwIQYJKoZIhvcNAQkBFhRpbmZvQHB5dGhvbi16ZWVwLm9yZzAe
+Fw0xNzAxMjUxOTI3NTJaFw0yNzAxMjMxOTI3NTJaMIGUMQswCQYDVQQGEwJOTDEQ
+MA4GA1UECBMHVXRyZWNodDEQMA4GA1UEBxMHVXRyZWNodDEeMBwGA1UEChMVTWlj
+aGFlbCB2YW4gVGVsbGluZ2VuMRwwGgYDVQQDExN3d3cucHl0aG9uLXplZXAub3Jn
+MSMwIQYJKoZIhvcNAQkBFhRpbmZvQHB5dGhvbi16ZWVwLm9yZzCCASIwDQYJKoZI
+hvcNAQEBBQADggEPADCCAQoCggEBAMq1sZUbZwE+6tiIFhGkFAsBvtIDbqkzT1It
+3y2f+1yO5TgXpk092HgmXO320y6wAR/JeDRHVxufAhWvzJHbJtOV7eBt2r62E/gj
+WQN7Tn+Nk7BiAef1b6nfS0uLoQVKNqqnE1M9VQPIz+wimNuXavESxHdYMN5S4zxq
+mGuvbFJBGMwAQriXz/cVBMki3nJcVsfpMtj6fAAFz6Q7ZRnW/a7M8WIUibXHvyhL
+G2amgkPWtmQCXhWriYlLzgzzYoLPL1ECxjWB3JhJuEr1ZEkoL6SnpAJNYAudTqi2
+MqafHPdep9QxtjwuW/ZE4+plF5AaGvY41iUGJBPMxucG2jO8QBsCAwEAAaOB/DCB
++TAdBgNVHQ4EFgQUxd12m9nIS0QO4uIPRy7oerPyVygwgckGA1UdIwSBwTCBvoAU
+xd12m9nIS0QO4uIPRy7oerPyVyihgZqkgZcwgZQxCzAJBgNVBAYTAk5MMRAwDgYD
+VQQIEwdVdHJlY2h0MRAwDgYDVQQHEwdVdHJlY2h0MR4wHAYDVQQKExVNaWNoYWVs
+IHZhbiBUZWxsaW5nZW4xHDAaBgNVBAMTE3d3dy5weXRob24temVlcC5vcmcxIzAh
+BgkqhkiG9w0BCQEWFGluZm9AcHl0aG9uLXplZXAub3JnggkAs4IF6fRL5bkwDAYD
+VR0TBAUwAwEB/zANBgkqhkiG9w0BAQUFAAOCAQEAHTUp/i9FYbvl86By7EvMlZeK
+v6I38IYcrIGzDdbrk8KkilYv7p2Ll8gUJYRFj96iX6Uvn0ACTutFJW9xE2ytBMOu
+UurTBpcpk8k368gfO/fGVi6HzjyFqTnhLkmd3CADIzPN/yg5j2q+mgA3ys6wISBR
+aDJR2jGt9sTAkAwkVJdDCFkCwyRfB28mBRnI5SLeR5vQyLT97THPma39xR3FaqYv
+h2q3coXBnaOOcuigiKyIynhJtXH42XlN3TM23b9NK2Oep2e51pxst3uohlDGmB/W
+uzx/hG+kNxy9D+Ms7qNL9+i4nHFOoR034RB/NGTChzTxq2JcXIKPWIo2tslNsg==
+-----END CERTIFICATE-----
diff --git a/tests/cert_valid_pw.pem b/tests/cert_valid_pw.pem
new file mode 100644
index 0000000..8b2656a
--- /dev/null
+++ b/tests/cert_valid_pw.pem
@@ -0,0 +1,58 @@
+-----BEGIN RSA PRIVATE KEY-----
+Proc-Type: 4,ENCRYPTED
+DEK-Info: DES-EDE3-CBC,415D98F4452C4F85
+
+5QDgVsTX78dXETR9RfVoAPXA3M0WKB6YibDZCavgMXBorWxOAaQz+chSehaJHHsi
+ZYWgxSUYsiZk5NVZLi3Pidg3zx8G1HWThxRmWakiEqseVTX9B/H2h1AEwd7AQ+5K
+kbLXbZ/CGTSkrVA+IjEWRtSjigaNfrDiyNO6YfLWjZrYk8mC2EVRnMD7LGDPL4E0
+rD9sFT4zAovZEe+WBGCWjzRiBMgIvY+4frsX3u9MkTYFxpNMkdDozUJu5sxr0asB
+68fSfLo2lDYuH9azWQe6aeMLkW/1QalRsYwGurZ/PW6DEnfkhN0ikdz0vpGARohL
+if+7wsSxKU6MRd5GntMqQ3uE+C5GxoiJ0T51+b/MDpo7hwhnhAqLvjPZNAcOtOip
+krLI3BPiyraBkY+km3EheDxX3XjGIl381577WO4HxKUtcT1KOYBLgN1VVdgrrlt3
+mLO9kexl6h57bi3OZxxanuX76zE7gvwVLgagMY/8Ek2TJJXhSne5YIrC5WPT1bQv
+nFenU8VzcF0xf02vTCos9Os6obeSJr3Lzz6StIj+WVytMD/QmfvG/0TCtlmq+av4
+VR7OWgnNcXWXPsoy6+8JwZLZGz3+Q2tav2JjGOCHneTIlgRyBKQX7sT6M8DGcLYf
+mZgysrA048Hh3bcPmk4W7p4/dHZBU1HR6KT34wKjMuLIEXuMVITfBK+Gv6Is9Q3h
+sVWJ2ChGcTkAIyqIaRglU0QBKUgOs4q2HsAxrefOXStaKlmawDjttFHhPveyKXCr
+NSqNMwK16g+9S1LcxCNoX8y1qTDObhGO/A3uTm81RcDN4gzQiZNd6ZM5ynuPyAAn
+8vACDiAXaf1SnAJjBuuqKm3dTXQgu+Ise7KmQXoherzHoXUZa0T49hCowb+WOJsB
+ljmEoehxVAPrgs8GmzvpSiJfCw2NgowaG75wMt8zTKAGgIht+R5xRNqp9g6Qgyn8
+0Z4E6JTZ8JRN9FtutUc0NKLuwiai2F4qwUzmOTPLiFTT6sAQrDX+JSmezPAVbtw0
+rchz/p1jCS+V0k78D/UwV/mGkXeqPUn370MVK0boPfi1ZrjASi6/ztaB7nza7QKP
+LCoZC7P6cbWqJG3g4MUxR/zDIdLF6XGBuSp2i6GmHlIi464AIv+O0Oz1ftIC/Xt2
+HZQoL7KW9NeU2WdIkczxQnPc/DucK/dkyKzzxLDr7ztU9E4maYTNd4Ml6B2azysY
+gyvAKq9ui8zYAZt096mACUSLj1/yNZKwlGUuyeq3XLaeqsHAGecEJsg3smnJ39Rd
+B4RSx3BDEQ4GiS1zNUaxs5KCCqtV4iylYWdUu6Y9H/koLuj04/c9qOjYCzIDDgTS
+M8asTkZIjXeknBb+RSTYj48Q1Vom2zjFhunPKvPmSgfdDGzX24ipagwZvmzLxlg1
+6edI75EBMMetVVwMWWe3ZU/AlERe/QVNBZUO2TwiVM2VvIVkaaa1pFYDv/eWeGLD
+HdJ1Hoz7O/gX6eKVY6OJu3nIwAXDTutxYC1VpZ6a48OrOWHuz352wkmw7aGcn0GS
+ZYjbizCZckvuDKk8PFpGbF7gPmaxtVKx/hijoqRJlRIJq9pSXTxbq7HEXPKq9dtI
+-----END RSA PRIVATE KEY-----
+
+-----BEGIN CERTIFICATE-----
+MIIEqjCCA5KgAwIBAgIJALOCBen0S+W5MA0GCSqGSIb3DQEBBQUAMIGUMQswCQYD
+VQQGEwJOTDEQMA4GA1UECBMHVXRyZWNodDEQMA4GA1UEBxMHVXRyZWNodDEeMBwG
+A1UEChMVTWljaGFlbCB2YW4gVGVsbGluZ2VuMRwwGgYDVQQDExN3d3cucHl0aG9u
+LXplZXAub3JnMSMwIQYJKoZIhvcNAQkBFhRpbmZvQHB5dGhvbi16ZWVwLm9yZzAe
+Fw0xNzAxMjUxOTI3NTJaFw0yNzAxMjMxOTI3NTJaMIGUMQswCQYDVQQGEwJOTDEQ
+MA4GA1UECBMHVXRyZWNodDEQMA4GA1UEBxMHVXRyZWNodDEeMBwGA1UEChMVTWlj
+aGFlbCB2YW4gVGVsbGluZ2VuMRwwGgYDVQQDExN3d3cucHl0aG9uLXplZXAub3Jn
+MSMwIQYJKoZIhvcNAQkBFhRpbmZvQHB5dGhvbi16ZWVwLm9yZzCCASIwDQYJKoZI
+hvcNAQEBBQADggEPADCCAQoCggEBAMq1sZUbZwE+6tiIFhGkFAsBvtIDbqkzT1It
+3y2f+1yO5TgXpk092HgmXO320y6wAR/JeDRHVxufAhWvzJHbJtOV7eBt2r62E/gj
+WQN7Tn+Nk7BiAef1b6nfS0uLoQVKNqqnE1M9VQPIz+wimNuXavESxHdYMN5S4zxq
+mGuvbFJBGMwAQriXz/cVBMki3nJcVsfpMtj6fAAFz6Q7ZRnW/a7M8WIUibXHvyhL
+G2amgkPWtmQCXhWriYlLzgzzYoLPL1ECxjWB3JhJuEr1ZEkoL6SnpAJNYAudTqi2
+MqafHPdep9QxtjwuW/ZE4+plF5AaGvY41iUGJBPMxucG2jO8QBsCAwEAAaOB/DCB
++TAdBgNVHQ4EFgQUxd12m9nIS0QO4uIPRy7oerPyVygwgckGA1UdIwSBwTCBvoAU
+xd12m9nIS0QO4uIPRy7oerPyVyihgZqkgZcwgZQxCzAJBgNVBAYTAk5MMRAwDgYD
+VQQIEwdVdHJlY2h0MRAwDgYDVQQHEwdVdHJlY2h0MR4wHAYDVQQKExVNaWNoYWVs
+IHZhbiBUZWxsaW5nZW4xHDAaBgNVBAMTE3d3dy5weXRob24temVlcC5vcmcxIzAh
+BgkqhkiG9w0BCQEWFGluZm9AcHl0aG9uLXplZXAub3JnggkAs4IF6fRL5bkwDAYD
+VR0TBAUwAwEB/zANBgkqhkiG9w0BAQUFAAOCAQEAHTUp/i9FYbvl86By7EvMlZeK
+v6I38IYcrIGzDdbrk8KkilYv7p2Ll8gUJYRFj96iX6Uvn0ACTutFJW9xE2ytBMOu
+UurTBpcpk8k368gfO/fGVi6HzjyFqTnhLkmd3CADIzPN/yg5j2q+mgA3ys6wISBR
+aDJR2jGt9sTAkAwkVJdDCFkCwyRfB28mBRnI5SLeR5vQyLT97THPma39xR3FaqYv
+h2q3coXBnaOOcuigiKyIynhJtXH42XlN3TM23b9NK2Oep2e51pxst3uohlDGmB/W
+uzx/hG+kNxy9D+Ms7qNL9+i4nHFOoR034RB/NGTChzTxq2JcXIKPWIo2tslNsg==
+-----END CERTIFICATE-----
diff --git a/tests/conftest.py b/tests/conftest.py
index fc8d077..4307ba8 100644
--- a/tests/conftest.py
+++ b/tests/conftest.py
@@ -1,5 +1,13 @@
+import sys
+
 import pytest
 
+# Don't try to test asyncio since it is py3 only syntax
+if sys.version_info < (3, 5):
+    collect_ignore = [
+        'test_asyncio_transport.py'
+    ]
+
 pytest.register_assert_rewrite('tests.utils')
 
 
@@ -12,3 +20,14 @@ def no_requests(request, monkeypatch):
         pytest.fail("External connections not allowed during tests.")
 
     monkeypatch.setattr("socket.socket", func)
+
+
+ at pytest.yield_fixture()
+def event_loop():
+    import asyncio
+
+    loop = asyncio.new_event_loop()
+    asyncio.set_event_loop(loop)
+
+    yield loop
+    loop.close()
diff --git a/tests/test_asyncio_transport.py b/tests/test_asyncio_transport.py
new file mode 100644
index 0000000..2c65a45
--- /dev/null
+++ b/tests/test_asyncio_transport.py
@@ -0,0 +1,41 @@
+import pytest
+from pretend import stub
+from lxml import etree
+from aioresponses import aioresponses
+
+from zeep import cache, asyncio
+
+
+ at pytest.mark.requests
+def test_no_cache(event_loop):
+    transport = asyncio.AsyncTransport(loop=event_loop)
+    assert transport.cache is None
+
+
+ at pytest.mark.requests
+def test_load(event_loop):
+    cache = stub(get=lambda url: None, add=lambda url, content: None)
+    transport = asyncio.AsyncTransport(loop=event_loop, cache=cache)
+
+    with aioresponses() as m:
+        m.get('http://tests.python-zeep.org/test.xml', body='x')
+        result = transport.load('http://tests.python-zeep.org/test.xml')
+        assert result == b'x'
+
+
+ at pytest.mark.requests
+ at pytest.mark.asyncio
+async def test_post(event_loop):
+    cache = stub(get=lambda url: None, add=lambda url, content: None)
+    transport = asyncio.AsyncTransport(loop=event_loop, cache=cache)
+
+    envelope = etree.Element('Envelope')
+
+    with aioresponses() as m:
+        m.post('http://tests.python-zeep.org/test.xml', body='x')
+        result = await transport.post_xml(
+            'http://tests.python-zeep.org/test.xml',
+            envelope=envelope,
+            headers={})
+
+        assert result.content == b'x'
diff --git a/tests/test_client.py b/tests/test_client.py
index 19dfb5c..6a45e85 100644
--- a/tests/test_client.py
+++ b/tests/test_client.py
@@ -2,12 +2,10 @@ import os
 
 import pytest
 import requests_mock
-from lxml import etree
 
-from zeep import client
-from zeep import xsd
-from zeep.exceptions import Error
 from tests.utils import load_xml
+from zeep import client, xsd
+from zeep.exceptions import Error
 
 
 def test_bind():
@@ -45,6 +43,13 @@ def test_service_proxy_non_existing():
         assert client_obj.service.NonExisting
 
 
+def test_open_from_file_object():
+    with open('tests/wsdl_files/soap_transport_err.wsdl', 'rb') as fh:
+        client_obj = client.Client(fh)
+        service = client_obj.bind()
+        assert service
+
+
 def test_client_no_wsdl():
     with pytest.raises(ValueError):
         client.Client(None)
diff --git a/tests/test_helpers.py b/tests/test_helpers.py
index 7165a5f..e34d4c6 100644
--- a/tests/test_helpers.py
+++ b/tests/test_helpers.py
@@ -1,7 +1,12 @@
+import datetime
+from collections import OrderedDict
+
 from lxml import etree
 
-from tests.utils import assert_nodes_equal, load_xml
+from tests.utils import assert_nodes_equal, load_xml, render_node
 from zeep import xsd
+from six import binary_type
+from zeep import helpers
 from zeep.helpers import serialize_object
 
 
@@ -147,3 +152,51 @@ def test_serialize_any_array():
     assert result == {
         '_value_1': [any_obj],
     }
+
+
+def test_create_xml_soap_map():
+    data = OrderedDict([
+        ('text', u'String'),
+        ('bytes', b'Bytes'),
+        ('boolean', True),
+        ('integer', 100),
+        ('float', 100.1234),
+        ('datetime', datetime.datetime(2017, 10, 28, 12, 30, 10)),
+        ('date', datetime.date(2016, 1, 14)),
+    ])
+    value = helpers.create_xml_soap_map(data)
+
+    expected = """
+    <document>
+        <item>
+          <key xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="xs:string">text</key>
+          <value xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="xs:string">String</value>
+        </item>
+        <item>
+          <key xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="xs:string">bytes</key>
+          <value xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="xs:string">Bytes</value>
+        </item>
+        <item>
+          <key xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="xs:string">boolean</key>
+          <value xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="xs:boolean">true</value>
+        </item>
+        <item>
+          <key xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="xs:string">integer</key>
+          <value xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="xs:integer">100</value>
+        </item>
+        <item>
+          <key xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="xs:string">float</key>
+          <value xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="xs:float">100.1234</value>
+        </item>
+        <item>
+          <key xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="xs:string">datetime</key>
+          <value xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="xs:dateTime">2017-10-28T12:30:10</value>
+        </item>
+        <item>
+          <key xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="xs:string">date</key>
+          <value xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="xs:date">2016-01-14</value>
+        </item>
+     </document>
+     """ # noqa
+    node = render_node(value._xsd_type, value)
+    assert_nodes_equal(expected, node)
diff --git a/tests/test_main.py b/tests/test_main.py
index de98d04..81b0623 100644
--- a/tests/test_main.py
+++ b/tests/test_main.py
@@ -28,4 +28,4 @@ def test_main_extract_auth(monkeypatch):
         assert mock_transport.call_count == 1
 
         args, kwargs = mock_transport.call_args
-        assert kwargs['http_auth'] == ('user', 'secret')
+        assert kwargs['session'].auth == ('user', 'secret')
diff --git a/tests/test_pprint.py b/tests/test_pprint.py
index 1c53780..ec9fea1 100644
--- a/tests/test_pprint.py
+++ b/tests/test_pprint.py
@@ -8,10 +8,10 @@ def test_dict():
         'foo_2': 'bar',
         'foo_3': 'bar',
         'foo_4': {
-            'bar': '1',
+            'foo': '1',
             'bar': {
                 'bala': 'qwe',
-            },
+                },
             'x': [1, 2, 3, 4],
             'y': [],
         }
diff --git a/tests/test_transports.py b/tests/test_transports.py
index 63f3e00..fa626d0 100644
--- a/tests/test_transports.py
+++ b/tests/test_transports.py
@@ -5,11 +5,6 @@ from pretend import stub
 from zeep import cache, transports
 
 
- at pytest.mark.requests
-def test_default_cache():
-    transport = transports.Transport()
-    assert isinstance(transport.cache, cache.SqliteCache)
-
 
 @pytest.mark.requests
 def test_no_cache():
@@ -18,6 +13,12 @@ def test_no_cache():
 
 
 @pytest.mark.requests
+def test_custom_cache():
+    transport = transports.Transport(cache=cache.SqliteCache())
+    assert isinstance(transport.cache, cache.SqliteCache)
+
+
+ at pytest.mark.requests
 def test_load():
     cache = stub(get=lambda url: None, add=lambda url, content: None)
     transport = transports.Transport(cache=cache)
diff --git a/tests/test_wsa.py b/tests/test_wsa.py
index a53d990..651528d 100644
--- a/tests/test_wsa.py
+++ b/tests/test_wsa.py
@@ -4,7 +4,7 @@ from pretend import stub
 from six import StringIO
 
 from tests.utils import DummyTransport, assert_nodes_equal
-from zeep import wsa, wsdl, Client
+from zeep import Client, wsa, wsdl
 
 
 def test_require_wsa(recwarn, monkeypatch):
diff --git a/tests/test_wsdl.py b/tests/test_wsdl.py
index 86aa0eb..f42ba4c 100644
--- a/tests/test_wsdl.py
+++ b/tests/test_wsdl.py
@@ -7,8 +7,7 @@ from pretend import stub
 from six import StringIO
 
 from tests.utils import DummyTransport, assert_nodes_equal
-from zeep import wsdl
-from zeep import Client
+from zeep import Client, wsdl
 from zeep.transports import Transport
 
 
@@ -36,6 +35,7 @@ def test_parse_soap_wsdl():
         m.post('http://example.com/stockquote', text=response)
         account_type = client.get_type('stoc:account')
         account = account_type(id=100)
+        account.user = 'mvantellingen'
         country = client.get_element('stoc:country').type()
         country.name = 'The Netherlands'
         country.code = 'NL'
@@ -58,7 +58,7 @@ def test_parse_soap_wsdl():
                 <tickerSymbol>foobar</tickerSymbol>
                 <account>
                   <id>100</id>
-                  <user/>
+                  <user>mvantellingen</user>
                 </account>
                 <stoc:country>
                   <name>The Netherlands</name>
@@ -324,10 +324,12 @@ def test_create_import_schema(recwarn):
     """.strip())
 
     transport = DummyTransport()
-    transport.bind('a.xsd', schema_node_a)
-    transport.bind('b.xsd', schema_node_b)
+    transport.bind('http://tests.python-zeep.org/a.xsd', schema_node_a)
+    transport.bind('http://tests.python-zeep.org/b.xsd', schema_node_b)
+
+    document = wsdl.Document(
+        content, transport, 'http://tests.python-zeep.org/content.wsdl')
 
-    document = wsdl.Document(content, transport)
     assert len(recwarn) == 0
     assert document.types.get_element('{http://tests.python-zeep.org/b}global')
 
@@ -366,10 +368,11 @@ def test_wsdl_imports_xsd(recwarn):
     """.strip())
 
     transport = DummyTransport()
-    transport.bind('a.xsd', schema_node_a)
-    transport.bind('b.xsd', schema_node_b)
+    transport.bind('http://tests.python-zeep.org/a.xsd', schema_node_a)
+    transport.bind('http://tests.python-zeep.org/b.xsd', schema_node_b)
 
-    wsdl.Document(content, transport)
+    wsdl.Document(
+        content, transport, 'http://tests.python-zeep.org/content.wsdl')
 
 
 def test_import_schema_without_location(recwarn):
@@ -445,10 +448,11 @@ def test_import_schema_without_location(recwarn):
     """.strip())
 
     transport = DummyTransport()
-    transport.bind('a.xsd', schema_node_a)
-    transport.bind('b.xsd', schema_node_b)
+    transport.bind('http://tests.python-zeep.org/a.xsd', schema_node_a)
+    transport.bind('http://tests.python-zeep.org/b.xsd', schema_node_b)
 
-    document = wsdl.Document(content, transport)
+    document = wsdl.Document(
+        content, transport, 'http://tests.python-zeep.org/content.wsdl')
     assert len(recwarn) == 0
     assert document.types.get_type('{http://tests.python-zeep.org/b}foo')
 
@@ -826,3 +830,73 @@ def test_parse_operation_empty_nodes():
     """.strip())
 
     assert wsdl.Document(content, None)
+
+
+def test_wsdl_duplicate_tns(recwarn):
+    wsdl_main = StringIO("""
+        <?xml version="1.0"?>
+        <wsdl:definitions
+          xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
+          xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+          xmlns:tns="http://tests.python-zeep.org/xsd-main"
+          xmlns:sec="http://tests.python-zeep.org/wsdl-secondary"
+          xmlns:xsd-sec="http://tests.python-zeep.org/xsd-secondary"
+          xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
+          xmlns:wsdlsoap="http://schemas.xmlsoap.org/wsdl/soap/"
+          targetNamespace="http://tests.python-zeep.org/xsd-main">
+
+          <wsdl:import namespace="http://tests.python-zeep.org/xsd-main"
+            location="http://tests.python-zeep.org/schema-2.wsdl"/>
+
+
+          <wsdl:binding name="TestBinding" type="tns:TestPortType">
+            <soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http"/>
+            <wsdl:operation name="TestOperation1">
+              <soap:operation soapAction=""/>
+              <wsdl:input>
+                <soap:body use="literal"/>
+              </wsdl:input>
+            </wsdl:operation>
+          </wsdl:binding>
+          <wsdl:service name="TestService">
+            <wsdl:documentation>Test service</wsdl:documentation>
+            <wsdl:port name="TestPortType" binding="tns:TestBinding">
+              <soap:address location="http://tests.python-zeep.org/test"/>
+            </wsdl:port>
+          </wsdl:service>
+        </wsdl:definitions>
+    """.strip())
+
+    wsdl_2 = ("""
+        <?xml version="1.0"?>
+        <wsdl:definitions
+          xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
+          xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+          xmlns:tns="http://tests.python-zeep.org/xsd-main"
+          xmlns:mine="http://tests.python-zeep.org/xsd-secondary"
+          xmlns:wsdlsoap="http://schemas.xmlsoap.org/wsdl/soap/"
+          targetNamespace="http://tests.python-zeep.org/xsd-main">
+
+          <wsdl:types>
+            <xsd:schema
+                targetNamespace="http://tests.python-zeep.org/xsd-main"
+                xmlns:tns="http://tests.python-zeep.org/xsd-main">
+              <xsd:element name="input" type="xsd:string"/>
+            </xsd:schema>
+          </wsdl:types>
+          <wsdl:message name="message-1">
+            <wsdl:part name="response" element="tns:input"/>
+          </wsdl:message>
+
+          <wsdl:portType name="TestPortType">
+            <wsdl:operation name="TestOperation1">
+              <wsdl:input message="message-1"/>
+            </wsdl:operation>
+          </wsdl:portType>
+        </wsdl:definitions>
+    """.strip())
+
+    transport = DummyTransport()
+    transport.bind('http://tests.python-zeep.org/schema-2.wsdl', wsdl_2)
+    document = wsdl.Document(wsdl_main, transport)
+    document.dump()
diff --git a/tests/test_wsdl_arrays.py b/tests/test_wsdl_arrays.py
index f6ad722..21b96d1 100644
--- a/tests/test_wsdl_arrays.py
+++ b/tests/test_wsdl_arrays.py
@@ -168,6 +168,15 @@ def test_complex_type_without_name():
         </document>
     """
     assert_nodes_equal(expected, node)
+    data = ArrayOfObject.parse_xmlelement(node, schema)
+
+    assert len(data._value_1) == 3
+    assert data._value_1[0]['attr_1'] == 'attr-1'
+    assert data._value_1[0]['attr_2'] == 'attr-2'
+    assert data._value_1[1]['attr_1'] == 'attr-3'
+    assert data._value_1[1]['attr_2'] == 'attr-4'
+    assert data._value_1[2]['attr_1'] == 'attr-5'
+    assert data._value_1[2]['attr_2'] == 'attr-6'
 
 
 def test_soap_array_parse_remote_ns():
diff --git a/tests/test_wsdl_messages_document.py b/tests/test_wsdl_messages_document.py
index 5aa18a7..c2a2389 100644
--- a/tests/test_wsdl_messages_document.py
+++ b/tests/test_wsdl_messages_document.py
@@ -56,13 +56,13 @@ def test_parse():
 
     assert operation.input.body.signature() == 'xsd:string'
     assert operation.input.header.signature() == ''
-    assert operation.input.envelope.signature() == 'body: xsd:string, header: {}'
+    assert operation.input.envelope.signature() == 'body: xsd:string'
     assert operation.input.signature(as_output=False) == 'xsd:string'
 
     assert operation.output.body.signature() == 'xsd:string'
     assert operation.output.header.signature() == ''
-    assert operation.output.envelope.signature() == 'body: xsd:string, header: {}'
-    assert operation.output.signature(as_output=True) == 'body: xsd:string, header: {}'
+    assert operation.output.envelope.signature() == 'body: xsd:string'
+    assert operation.output.signature(as_output=True) == 'xsd:string'
 
 
 def test_empty_input_parse():
@@ -113,7 +113,7 @@ def test_empty_input_parse():
 
     assert operation.input.body.signature() == ''
     assert operation.input.header.signature() == ''
-    assert operation.input.envelope.signature() == 'body: {}, header: {}'
+    assert operation.input.envelope.signature() == 'body: {}'
     assert operation.input.signature(as_output=False) == ''
 
 
@@ -251,9 +251,6 @@ def test_parse_with_header_type():
     assert operation.output.signature(as_output=True) == 'body: xsd:string, header: {auth: ResponseHeaderType}'  # noqa
 
 
-
-
-
 def test_parse_with_header_other_message():
     wsdl_content = StringIO("""
     <definitions xmlns="http://schemas.xmlsoap.org/wsdl/"
diff --git a/tests/test_wsdl_messages_rpc.py b/tests/test_wsdl_messages_rpc.py
index 123683d..1365704 100644
--- a/tests/test_wsdl_messages_rpc.py
+++ b/tests/test_wsdl_messages_rpc.py
@@ -153,7 +153,7 @@ def test_deserialize():
           </soap-env:Body>
         </soap-env:Envelope>
     """)
-    assert operation.output.signature(True) == 'body: {result: xsd:string}, header: {}'
+    assert operation.output.signature(True) == 'result: xsd:string'
     result = operation.output.deserialize(document)
     assert result == 'ah1'
 
@@ -381,7 +381,7 @@ def test_deserialize_rpc_literal():
     assert deserialized == 'foobar'
 
 
-def test_deserialize():
+def test_deserialize_x():
     wsdl_content = StringIO("""
     <definitions xmlns="http://schemas.xmlsoap.org/wsdl/"
                  xmlns:tns="http://tests.python-zeep.org/tns"
@@ -398,8 +398,10 @@ def test_deserialize():
 
       <portType name="TestPortType">
         <operation name="clearFoo">
-          <input wsam:Action="http://foo.services.example.com/Util/clearFooRequest" message="tns:clearFoo"/>
-          <output wsam:Action="http://foo.services.example.com/Util/clearFooResponse" message="tns:clearFooResponse"/>
+          <input wsam:Action="http://foo.services.example.com/Util/clearFooRequest"
+                 message="tns:clearFoo"/>
+          <output wsam:Action="http://foo.services.example.com/Util/clearFooResponse"
+                  message="tns:clearFooResponse"/>
         </operation>
       </portType>
 
diff --git a/tests/test_wsdl_soap.py b/tests/test_wsdl_soap.py
index 04a9fc9..960645f 100644
--- a/tests/test_wsdl_soap.py
+++ b/tests/test_wsdl_soap.py
@@ -1,6 +1,8 @@
 from lxml import etree
+from pretend import stub
 
 from tests.utils import load_xml
+from zeep import Client
 from zeep.exceptions import Fault
 from zeep.wsdl import bindings
 
@@ -108,3 +110,60 @@ def test_soap12_process_error():
         assert exc.subcodes[0].localname == 'fault-subcode1'
         assert exc.subcodes[1].namespace == 'http://example.com/example2'
         assert exc.subcodes[1].localname == 'fault-subcode2'
+
+
+def test_mime_multipart():
+    data = '\r\n'.join(line.strip() for line in """
+        --MIME_boundary
+        Content-Type: text/xml; charset=UTF-8
+        Content-Transfer-Encoding: 8bit
+        Content-ID: <claim061400a.xml at claiming-it.com>
+
+        <?xml version='1.0' ?>
+        <SOAP-ENV:Envelope
+        xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
+        <SOAP-ENV:Body>
+        <claim:insurance_claim_auto id="insurance_claim_document_id"
+        xmlns:claim="http://schemas.risky-stuff.com/Auto-Claim">
+        <theSignedForm href="cid:claim061400a.tiff at claiming-it.com"/>
+        <theCrashPhoto href="cid:claim061400a.jpeg at claiming-it.com"/>
+        <!-- ... more claim details go here... -->
+        </claim:insurance_claim_auto>
+        </SOAP-ENV:Body>
+        </SOAP-ENV:Envelope>
+
+        --MIME_boundary
+        Content-Type: image/tiff
+        Content-Transfer-Encoding: base64
+        Content-ID: <claim061400a.tiff at claiming-it.com>
+
+        Li4uQmFzZTY0IGVuY29kZWQgVElGRiBpbWFnZS4uLg==
+
+        --MIME_boundary
+        Content-Type: image/jpeg
+        Content-Transfer-Encoding: binary
+        Content-ID: <claim061400a.jpeg at claiming-it.com>
+
+        ...Raw JPEG image..
+        --MIME_boundary--
+    """.splitlines()).encode('utf-8')
+
+    client = Client('tests/wsdl_files/claim.wsdl')
+    binding = client.service._binding
+
+    response = stub(
+        status_code=200,
+        content=data,
+        headers={
+            'Content-Type': 'multipart/related; type="text/xml"; start="<claim061400a.xml at claiming-it.com>"; boundary="MIME_boundary"'
+        }
+    )
+
+    result = binding.process_reply(
+        client, binding.get('GetClaimDetails'), response)
+
+    assert result.root is None
+    assert len(result.attachments) == 2
+
+    assert result.attachments[0].content == b'...Base64 encoded TIFF image...'
+    assert result.attachments[1].content == b'...Raw JPEG image..'
diff --git a/tests/test_wsse_signature.py b/tests/test_wsse_signature.py
new file mode 100644
index 0000000..503e90e
--- /dev/null
+++ b/tests/test_wsse_signature.py
@@ -0,0 +1,112 @@
+import os
+import sys
+
+import pytest
+
+from tests.utils import load_xml
+from zeep.exceptions import SignatureVerificationFailed
+from zeep import wsse
+from zeep.wsse import signature
+
+DS_NS = 'http://www.w3.org/2000/09/xmldsig#'
+
+
+KEY_FILE = os.path.join(
+    os.path.dirname(os.path.realpath(__file__)), 'cert_valid.pem')
+KEY_FILE_PW = os.path.join(
+    os.path.dirname(os.path.realpath(__file__)), 'cert_valid_pw.pem')
+
+
+ at pytest.mark.skipif(sys.platform == 'win32',
+                    reason="does not run on windows")
+def test_sign():
+    envelope = load_xml("""
+        <soapenv:Envelope
+            xmlns:tns="http://tests.python-zeep.org/"
+            xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
+            xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
+            xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/">
+          <soapenv:Header></soapenv:Header>
+          <soapenv:Body>
+            <tns:Function>
+              <tns:Argument>OK</tns:Argument>
+            </tns:Function>
+          </soapenv:Body>
+        </soapenv:Envelope>
+    """)
+
+    signature.sign_envelope(envelope, KEY_FILE, KEY_FILE)
+    signature.verify_envelope(envelope, KEY_FILE)
+
+
+ at pytest.mark.skipif(sys.platform == 'win32',
+                    reason="does not run on windows")
+def test_sign_pw():
+    envelope = load_xml("""
+        <soapenv:Envelope
+            xmlns:tns="http://tests.python-zeep.org/"
+            xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
+            xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
+            xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/">
+          <soapenv:Header></soapenv:Header>
+          <soapenv:Body>
+            <tns:Function>
+              <tns:Argument>OK</tns:Argument>
+            </tns:Function>
+          </soapenv:Body>
+        </soapenv:Envelope>
+    """)
+
+    signature.sign_envelope(envelope, KEY_FILE_PW, KEY_FILE_PW, 'geheim')
+    signature.verify_envelope(envelope, KEY_FILE_PW)
+
+
+ at pytest.mark.skipif(sys.platform == 'win32',
+                    reason="does not run on windows")
+def test_verify_error():
+    envelope = load_xml("""
+        <soapenv:Envelope
+            xmlns:tns="http://tests.python-zeep.org/"
+            xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
+            xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
+            xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/">
+          <soapenv:Header></soapenv:Header>
+          <soapenv:Body>
+            <tns:Function>
+              <tns:Argument>OK</tns:Argument>
+            </tns:Function>
+          </soapenv:Body>
+        </soapenv:Envelope>
+    """)
+
+    signature.sign_envelope(envelope, KEY_FILE, KEY_FILE)
+    nsmap = {'tns': 'http://tests.python-zeep.org/'}
+
+    for elm in envelope.xpath('//tns:Argument', namespaces=nsmap):
+        elm.text = 'NOT!'
+
+    with pytest.raises(SignatureVerificationFailed):
+        signature.verify_envelope(envelope, KEY_FILE)
+
+
+ at pytest.mark.skipif(sys.platform == 'win32',
+                    reason="does not run on windows")
+def test_signature():
+    envelope = load_xml("""
+        <soapenv:Envelope
+            xmlns:tns="http://tests.python-zeep.org/"
+            xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
+            xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
+            xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/">
+          <soapenv:Header></soapenv:Header>
+          <soapenv:Body>
+            <tns:Function>
+              <tns:Argument>OK</tns:Argument>
+            </tns:Function>
+          </soapenv:Body>
+        </soapenv:Envelope>
+    """)
+
+    plugin = wsse.Signature(KEY_FILE_PW, KEY_FILE_PW, 'geheim')
+    envelope, headers = plugin.apply(envelope, {})
+    plugin.verify(envelope)
diff --git a/tests/test_wsse_username.py b/tests/test_wsse_username.py
index 690b818..586097a 100644
--- a/tests/test_wsse_username.py
+++ b/tests/test_wsse_username.py
@@ -7,7 +7,7 @@ from freezegun import freeze_time
 
 from tests.utils import assert_nodes_equal, load_xml
 from zeep import client
-from zeep.wsse.username import UsernameToken
+from zeep.wsse import UsernameToken
 
 
 @pytest.mark.requests
@@ -55,7 +55,7 @@ def test_password_text():
     """)
 
     token = UsernameToken('michael', 'geheim')
-    envelope, headers = token.sign(envelope, {})
+    envelope, headers = token.apply(envelope, {})
     expected = """
         <soap-env:Envelope
             xmlns:ns0="http://example.com/stockquote.xsd"
@@ -64,12 +64,12 @@ def test_password_text():
             xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
             xmlns:xsd="http://www.w3.org/2001/XMLSchema">
           <soap-env:Header>
-            <ns0:Security xmlns:ns0="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
-              <ns0:UsernameToken>
-                <ns0:Username>michael</ns0:Username>
-                <ns0:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText">geheim</ns0:Password>
-              </ns0:UsernameToken>
-            </ns0:Security>
+            <wsse:Security xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
+              <wsse:UsernameToken>
+                <wsse:Username>michael</wsse:Username>
+                <wsse:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText">geheim</wsse:Password>
+              </wsse:UsernameToken>
+            </wsse:Security>
           </soap-env:Header>
           <soap-env:Body>
             <ns0:TradePriceRequest>
@@ -104,7 +104,7 @@ def test_password_digest(monkeypatch):
     """)
 
     token = UsernameToken('michael', 'geheim', use_digest=True)
-    envelope, headers = token.sign(envelope, {})
+    envelope, headers = token.apply(envelope, {})
     expected = """
         <soap-env:Envelope
             xmlns:ns0="http://example.com/stockquote.xsd"
@@ -113,14 +113,14 @@ def test_password_digest(monkeypatch):
             xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
             xmlns:xsd="http://www.w3.org/2001/XMLSchema">
           <soap-env:Header>
-            <ns0:Security xmlns:ns0="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
-              <ns0:UsernameToken>
-                <ns0:Username>michael</ns0:Username>
-                <ns0:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordDigest">hVicspAQSg70JNhe67OHqD9gexc=</ns0:Password>
-                <ns0:Nonce EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary">bW9ja2VkLXJhbmRvbQ==</ns0:Nonce>
-                <ns0:Created xmlns:ns0="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">2016-05-08T12:00:00+00:00</ns0:Created>
-              </ns0:UsernameToken>
-            </ns0:Security>
+            <wsse:Security xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
+              <wsse:UsernameToken>
+                <wsse:Username>michael</wsse:Username>
+                <wsse:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordDigest">hVicspAQSg70JNhe67OHqD9gexc=</wsse:Password>
+                <wsse:Nonce EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary">bW9ja2VkLXJhbmRvbQ==</wsse:Nonce>
+                <wsu:Created xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">2016-05-08T12:00:00+00:00</wsu:Created>
+              </wsse:UsernameToken>
+            </wsse:Security>
           </soap-env:Header>
           <soap-env:Body>
             <ns0:TradePriceRequest>
@@ -158,7 +158,7 @@ def test_password_digest_custom(monkeypatch):
     token = UsernameToken(
         'michael', password_digest='12345', use_digest=True,
         nonce='iets', created=created)
-    envelope, headers = token.sign(envelope, {})
+    envelope, headers = token.apply(envelope, {})
     expected = """
         <soap-env:Envelope
             xmlns:ns0="http://example.com/stockquote.xsd"
@@ -167,14 +167,14 @@ def test_password_digest_custom(monkeypatch):
             xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
             xmlns:xsd="http://www.w3.org/2001/XMLSchema">
           <soap-env:Header>
-            <ns0:Security xmlns:ns0="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
-              <ns0:UsernameToken>
-                <ns0:Username>michael</ns0:Username>
-                <ns0:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordDigest">12345</ns0:Password>
-                <ns0:Nonce EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary">aWV0cw==</ns0:Nonce>
-                <ns0:Created xmlns:ns0="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">2016-06-04T20:10:00+00:00</ns0:Created>
-              </ns0:UsernameToken>
-            </ns0:Security>
+            <wsse:Security xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
+              <wsse:UsernameToken>
+                <wsse:Username>michael</wsse:Username>
+                <wsse:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordDigest">12345</wsse:Password>
+                <wsse:Nonce EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary">aWV0cw==</wsse:Nonce>
+                <wsu:Created xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">2016-06-04T20:10:00+00:00</wsu:Created>
+              </wsse:UsernameToken>
+            </wsse:Security>
           </soap-env:Header>
           <soap-env:Body>
             <ns0:TradePriceRequest>
@@ -211,7 +211,7 @@ def test_password_prepared():
     """)  # noqa
 
     token = UsernameToken('michael', 'geheim')
-    envelope, headers = token.sign(envelope, {})
+    envelope, headers = token.apply(envelope, {})
     expected = """
         <soap-env:Envelope
             xmlns:ns0="http://example.com/stockquote.xsd"
diff --git a/tests/test_xsd.py b/tests/test_xsd.py
index 311f082..573784a 100644
--- a/tests/test_xsd.py
+++ b/tests/test_xsd.py
@@ -304,7 +304,8 @@ def test_choice_init():
             {'item_2': 'value-2'},
             {'item_1': 'value-3'},
             {'item_4_1': 'value-4-1', 'item_4_2': 'value-4-2'},
-        ])
+        ],
+        post='bar')
 
     assert obj._value_1 == [
         {'item_1': 'value-1'},
@@ -326,7 +327,7 @@ def test_choice_init():
         <ns0:item_1>value-3</ns0:item_1>
         <ns0:item_4_1>value-4-1</ns0:item_4_1>
         <ns0:item_4_2>value-4-2</ns0:item_4_2>
-        <ns0:post/>
+        <ns0:post>bar</ns0:post>
       </ns0:kies>
     </document>
     """.strip()
diff --git a/tests/test_xsd_any.py b/tests/test_xsd_any.py
index 4137b34..1595b2c 100644
--- a/tests/test_xsd_any.py
+++ b/tests/test_xsd_any.py
@@ -135,7 +135,8 @@ def test_any_with_ref():
     container_elm = schema.get_element('{http://tests.python-zeep.org/}container')
     obj = container_elm(
         item='bar',
-        _value_1=xsd.AnyObject(item_elm, item_elm('argh')))
+        _value_1=xsd.AnyObject(item_elm, item_elm('argh')),
+        _value_2=xsd.AnyObject(item_elm, item_elm('ok')))
 
     node = etree.Element('document')
     container_elm.render(node, obj)
@@ -144,6 +145,7 @@ def test_any_with_ref():
             <ns0:container xmlns:ns0="http://tests.python-zeep.org/">
                 <ns0:item>bar</ns0:item>
                 <ns0:item>argh</ns0:item>
+                <ns0:item>ok</ns0:item>
             </ns0:container>
         </document>
     """
@@ -218,6 +220,54 @@ def test_element_any_type():
     assert item.something == 'bar'
 
 
+def test_element_any_type_elements():
+    node = etree.fromstring("""
+        <?xml version="1.0"?>
+        <schema xmlns="http://www.w3.org/2001/XMLSchema"
+                xmlns:tns="http://tests.python-zeep.org/"
+                targetNamespace="http://tests.python-zeep.org/"
+                elementFormDefault="qualified">
+          <element name="container">
+            <complexType>
+              <sequence>
+                <element name="something" type="anyType"/>
+              </sequence>
+            </complexType>
+          </element>
+        </schema>
+    """.strip())
+    schema = xsd.Schema(node)
+
+    Child = xsd.ComplexType(
+        xsd.Sequence([
+            xsd.Element('{http://tests.python-zeep.org/}item_1', xsd.String()),
+            xsd.Element('{http://tests.python-zeep.org/}item_2', xsd.String()),
+        ])
+    )
+    child = Child(item_1='item-1', item_2='item-2')
+
+    container_elm = schema.get_element('{http://tests.python-zeep.org/}container')
+    obj = container_elm(something=child)
+
+    node = etree.Element('document')
+    container_elm.render(node, obj)
+    expected = """
+        <document>
+            <ns0:container xmlns:ns0="http://tests.python-zeep.org/">
+                <ns0:something>
+                    <ns0:item_1>item-1</ns0:item_1>
+                    <ns0:item_2>item-2</ns0:item_2>
+                </ns0:something>
+            </ns0:container>
+        </document>
+    """
+    assert_nodes_equal(expected, node)
+    item = container_elm.parse(node.getchildren()[0], schema)
+    assert len(item.something) == 2
+    assert item.something[0].text == 'item-1'
+    assert item.something[1].text == 'item-2'
+
+
 def test_any_in_nested_sequence():
     schema = xsd.Schema(load_xml("""
         <?xml version="1.0"?>
diff --git a/tests/test_xsd_attributes.py b/tests/test_xsd_attributes.py
index 3e42458..f30d76e 100644
--- a/tests/test_xsd_attributes.py
+++ b/tests/test_xsd_attributes.py
@@ -418,3 +418,48 @@ def test_attribute_union_type_inline():
 
     attr = schema.get_attribute('{http://tests.python-zeep.org/}something')
     assert attr('foo') == 'foo'
+
+
+def test_attribute_value_retrieval():
+    schema = xsd.Schema(load_xml("""
+        <?xml version="1.0"?>
+        <schema xmlns="http://www.w3.org/2001/XMLSchema"
+                xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+                xmlns:tns="http://tests.python-zeep.org/"
+                elementFormDefault="qualified"
+                targetNamespace="http://tests.python-zeep.org/">
+          <complexType name="Address">
+            <sequence>
+              <element name="Street" type="tns:Street"/>
+            </sequence>
+          </complexType>
+          <complexType name="Street">
+            <sequence>
+                <element name="Name" type="string"/>
+                <element name="Something" type="string" minOccurs="0"/>
+            </sequence>
+            <attribute name="ID" type="int" use="required"/>
+            <attribute name="Postcode" type="string"/>
+          </complexType>
+        </schema>
+    """))
+
+    Addr = schema.get_type('{http://tests.python-zeep.org/}Address')
+
+    address = Addr()
+    address.Street = {
+        'ID': 100,
+        'Name': 'Foo',
+    }
+
+    expected = """
+      <document>
+        <ns0:Street xmlns:ns0="http://tests.python-zeep.org/" ID="100">
+            <ns0:Name>Foo</ns0:Name>
+        </ns0:Street>
+      </document>
+    """
+
+    node = etree.Element('document')
+    Addr.render(node, address)
+    assert_nodes_equal(expected, node)
diff --git a/tests/test_xsd_builtins.py b/tests/test_xsd_builtins.py
index a10951d..abbab75 100644
--- a/tests/test_xsd_builtins.py
+++ b/tests/test_xsd_builtins.py
@@ -6,7 +6,7 @@ import pytest
 import pytz
 import six
 
-from zeep.xsd import builtins
+from zeep.xsd.types import builtins
 
 
 class TestString:
diff --git a/tests/test_xsd_choice.py b/tests/test_xsd_choice.py
index 7478962..cff9296 100644
--- a/tests/test_xsd_choice.py
+++ b/tests/test_xsd_choice.py
@@ -321,12 +321,12 @@ def test_choice_in_sequence():
 
     assert container_elm.type.signature() == (
         'something: xsd:string, ({item_1: xsd:string} | {item_2: xsd:string} | {item_3: xsd:string})')  # noqa
-    value = container_elm(item_1='item-1')
+    value = container_elm(something='foobar', item_1='item-1')
 
     expected = """
       <document>
         <ns0:container xmlns:ns0="http://tests.python-zeep.org/">
-          <ns0:something/>
+          <ns0:something>foobar</ns0:something>
           <ns0:item_1>item-1</ns0:item_1>
         </ns0:container>
       </document>
@@ -977,3 +977,69 @@ def test_parse_check_mixed_choices():
       </document>
     """
     assert_nodes_equal(expected, node)
+
+
+def test_choice_extend():
+    schema = xsd.Schema(load_xml("""
+        <?xml version="1.0"?>
+        <schema
+                xmlns="http://www.w3.org/2001/XMLSchema"
+                xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+                xmlns:tns="http://tests.python-zeep.org/"
+                elementFormDefault="qualified"
+                targetNamespace="http://tests.python-zeep.org/">
+            <xsd:complexType name="BaseType">
+                <xsd:sequence>
+                    <xsd:element name="optional" minOccurs="0"/>
+                </xsd:sequence>
+                <xsd:attribute name="Id"/>
+            </xsd:complexType>
+            <xsd:complexType name="ChildType">
+                <xsd:complexContent>
+                    <xsd:extension base="tns:BaseType">
+                        <xsd:sequence>
+                            <xsd:element name="item-1-1" type="xsd:string"/>
+                            <xsd:element name="item-1-2" type="xsd:string"/>
+                        </xsd:sequence>
+                    </xsd:extension>
+                </xsd:complexContent>
+            </xsd:complexType>
+            <xsd:element name="container">
+                <xsd:complexType>
+                    <xsd:complexContent>
+                        <xsd:extension base="tns:ChildType">
+                            <xsd:choice minOccurs="0" maxOccurs="6">
+                                <xsd:element name="item-2-1" type="xsd:string"/>
+                                <xsd:element name="item-2-2" type="xsd:string"/>
+                            </xsd:choice>
+                            <xsd:attribute name="version" use="required" fixed="10.0.1.2"/>
+                        </xsd:extension>
+                    </xsd:complexContent>
+                </xsd:complexType>
+            </xsd:element>
+        </schema>
+    """))
+
+    element = schema.get_element('ns0:container')
+    node = load_xml("""
+        <ns0:container xmlns:ns0="http://tests.python-zeep.org/">
+          <ns0:item-1-1>foo</ns0:item-1-1>
+          <ns0:item-1-2>bar</ns0:item-1-2>
+        </ns0:container>
+    """)
+    value = element.parse(node, schema)
+
+    node = load_xml("""
+        <ns0:container xmlns:ns0="http://tests.python-zeep.org/">
+          <ns0:item-1-1>foo</ns0:item-1-1>
+          <ns0:item-1-2>bar</ns0:item-1-2>
+          <ns0:item-2-1>xafoo</ns0:item-2-1>
+          <ns0:item-2-2>xabar</ns0:item-2-2>
+
+        </ns0:container>
+    """)
+    value = element.parse(node, schema)
+    assert value['item-1-1'] == 'foo'
+    assert value['item-1-2'] == 'bar'
+    assert value['_value_1'][0] == {'item-2-1': 'xafoo'}
+    assert value['_value_1'][1] == {'item-2-2': 'xabar'}
diff --git a/tests/test_xsd_complex_types.py b/tests/test_xsd_complex_types.py
index cdfbde3..c22f185 100644
--- a/tests/test_xsd_complex_types.py
+++ b/tests/test_xsd_complex_types.py
@@ -1,6 +1,6 @@
 import pytest
-
 from lxml import etree
+
 from tests.utils import assert_nodes_equal, load_xml, render_node
 from zeep import xsd
 
diff --git a/tests/test_xsd_extension.py b/tests/test_xsd_extension.py
index d8c5a8f..206b5f0 100644
--- a/tests/test_xsd_extension.py
+++ b/tests/test_xsd_extension.py
@@ -3,7 +3,8 @@ import io
 
 from lxml import etree
 
-from tests.utils import DummyTransport, assert_nodes_equal, load_xml, render_node
+from tests.utils import (
+    DummyTransport, assert_nodes_equal, load_xml, render_node)
 from zeep import xsd
 
 
@@ -106,7 +107,7 @@ def test_complex_content_with_recursive_elements():
               <xsd:complexContent>
                 <xsd:extension base="tns:Name">
                   <xsd:sequence>
-                    <xsd:element name="children" type="tns:Pet"/>
+                    <xsd:element name="children" type="tns:Pet" minOccurs="0" maxOccurs="unbounded"/>
                   </xsd:sequence>
                 </xsd:extension>
               </xsd:complexContent>
@@ -125,7 +126,10 @@ def test_complex_content_with_recursive_elements():
     assert(pet_type.signature() == 'name: xsd:string, common_name: xsd:string, children: Pet')
 
     obj = pet_type(
-        name='foo', common_name='bar')
+        name='foo', common_name='bar',
+        children=[
+            pet_type(name='child-1', common_name='child-cname-1')
+        ])
 
     node = etree.Element('document')
     pet_type.render(node, obj)
@@ -134,7 +138,10 @@ def test_complex_content_with_recursive_elements():
             <ns0:Pet xmlns:ns0="http://tests.python-zeep.org/">
                 <ns0:name>foo</ns0:name>
                 <ns0:common_name>bar</ns0:common_name>
-                <ns0:children/>
+                <ns0:children>
+                    <ns0:name>child-1</ns0:name>
+                    <ns0:common_name>child-cname-1</ns0:common_name>
+                </ns0:children>
             </ns0:Pet>
         </document>
     """
@@ -543,14 +550,14 @@ def test_complex_content_extension_with_sequence():
     address_type = schema.get_element('{http://tests.python-zeep.org/}SpecialPackage')
 
     obj = address_type(
-        id='testString', pkg_id='nameId')
+        id='testString', pkg_id='nameId', otherElement='foobar')
 
     node = etree.Element('document')
     address_type.render(node, obj)
     expected = """
         <document>
             <ns0:SpecialPackage xmlns:ns0="http://tests.python-zeep.org/" pkg_id="nameId" id="testString">
-                <ns0:otherElement/>
+                <ns0:otherElement>foobar</ns0:otherElement>
             </ns0:SpecialPackage>
         </document>
     """
diff --git a/tests/test_xsd_integration.py b/tests/test_xsd_integration.py
index 64d9847..b262b48 100644
--- a/tests/test_xsd_integration.py
+++ b/tests/test_xsd_integration.py
@@ -774,7 +774,7 @@ def test_complex_type_empty():
     schema = xsd.Schema(node)
 
     container_elm = schema.get_element('{http://tests.python-zeep.org/}container')
-    obj = container_elm()
+    obj = container_elm(something={})
 
     node = etree.Element('document')
     container_elm.render(node, obj)
diff --git a/tests/test_xsd_parse.py b/tests/test_xsd_parse.py
index 3357d7c..aa0e76c 100644
--- a/tests/test_xsd_parse.py
+++ b/tests/test_xsd_parse.py
@@ -1,4 +1,5 @@
 import datetime
+
 from lxml import etree
 
 from tests.utils import load_xml
@@ -238,7 +239,7 @@ def test_sequence_parse_anytype_obj():
             '{http://www.w3.org/2001/XMLSchema}Schema',
             targetNamespace='http://tests.python-zeep.org/'))
 
-    root = list(schema._schemas.values())[0]
+    root = next(schema.documents)
     root.register_type('{http://tests.python-zeep.org/}something', value_type)
 
     custom_type = xsd.Element(
diff --git a/tests/test_xsd_schemas.py b/tests/test_xsd_schemas.py
index 269c3fe..a629e46 100644
--- a/tests/test_xsd_schemas.py
+++ b/tests/test_xsd_schemas.py
@@ -3,8 +3,7 @@ from lxml import etree
 
 from tests.utils import DummyTransport, load_xml
 from zeep import exceptions, xsd
-from zeep.xsd.builtins import Schema as Schema
-from zeep.exceptions import ZeepWarning
+from zeep.xsd import Schema
 
 
 def test_default_types():
@@ -125,7 +124,9 @@ def test_schema_doc_repr_val():
             elementFormDefault="qualified">
         </xs:schema>
     """))
-    doc = schema._get_schema_document('http://tests.python-zeep.org/')
+    docs = schema._get_schema_documents('http://tests.python-zeep.org/')
+    assert len(docs) == 1
+    doc = docs[0]
     assert repr(doc) == "<SchemaDocument(location=None, tns='http://tests.python-zeep.org/', is_empty=True)>"
 
 
@@ -393,8 +394,16 @@ def test_duplicate_target_namespace():
         <?xml version="1.0"?>
         <xsd:schema
             xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+            xmlns:tns="http://tests.python-zeep.org/duplicate"
             targetNamespace="http://tests.python-zeep.org/duplicate"
             elementFormDefault="qualified">
+            <xsd:element name="elm-in-b" type="tns:item-c"/>
+            <xsd:complexType name="item-c">
+              <xsd:sequence>
+                <xsd:element name="item-a" type="xsd:string"/>
+                <xsd:element name="item-b" type="xsd:string"/>
+              </xsd:sequence>
+            </xsd:complexType>
         </xsd:schema>
     """.strip())
 
@@ -402,8 +411,17 @@ def test_duplicate_target_namespace():
         <?xml version="1.0"?>
         <xsd:schema
             xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+            xmlns:tns="http://tests.python-zeep.org/duplicate"
             targetNamespace="http://tests.python-zeep.org/duplicate"
             elementFormDefault="qualified">
+            <xsd:element name="elm-in-c" type="tns:item-c"/>
+            <xsd:complexType name="item-c">
+              <xsd:sequence>
+                <xsd:element name="item-a" type="xsd:string"/>
+                <xsd:element name="item-b" type="xsd:string"/>
+              </xsd:sequence>
+            </xsd:complexType>
+
         </xsd:schema>
     """.strip())
 
@@ -411,8 +429,12 @@ def test_duplicate_target_namespace():
     transport.bind('http://tests.python-zeep.org/a.xsd', schema_a)
     transport.bind('http://tests.python-zeep.org/b.xsd', schema_b)
     transport.bind('http://tests.python-zeep.org/c.xsd', schema_c)
-    with pytest.warns(ZeepWarning):
-        xsd.Schema(schema_a, transport=transport)
+    schema = xsd.Schema(schema_a, transport=transport)
+
+    elm_b = schema.get_element('{http://tests.python-zeep.org/duplicate}elm-in-b')
+    elm_c = schema.get_element('{http://tests.python-zeep.org/duplicate}elm-in-c')
+    assert not isinstance(elm_b.type, xsd.UnresolvedType)
+    assert not isinstance(elm_c.type, xsd.UnresolvedType)
 
 
 def test_multiple_no_namespace():
@@ -440,8 +462,7 @@ def test_multiple_no_namespace():
     transport = DummyTransport()
     transport.bind('http://tests.python-zeep.org/b.xsd', node_b)
     transport.bind('http://tests.python-zeep.org/c.xsd', node_b)
-    with pytest.warns(ZeepWarning):
-        xsd.Schema(node_a, transport=transport)
+    xsd.Schema(node_a, transport=transport)
 
 
 def test_multiple_only_target_ns():
@@ -470,8 +491,7 @@ def test_multiple_only_target_ns():
     transport = DummyTransport()
     transport.bind('http://tests.python-zeep.org/b.xsd', node_b)
     transport.bind('http://tests.python-zeep.org/c.xsd', node_b)
-    with pytest.warns(ZeepWarning):
-        xsd.Schema(node_a, transport=transport)
+    xsd.Schema(node_a, transport=transport)
 
 
 def test_schema_error_handling():
diff --git a/tests/test_xsd_types.py b/tests/test_xsd_types.py
index 5e902fa..b5a14e8 100644
--- a/tests/test_xsd_types.py
+++ b/tests/test_xsd_types.py
@@ -27,51 +27,51 @@ def test_base_type():
 
 
 def test_simpletype_eq():
-    type_1 = types.SimpleType()
-    type_2 = types.SimpleType()
+    type_1 = types.AnySimpleType()
+    type_2 = types.AnySimpleType()
 
     assert type_1 == type_2
 
 
 def test_simpletype_parse():
     node = etree.Element('foobar')
-    item = types.SimpleType()
+    item = types.AnySimpleType()
 
     assert item.parse_xmlelement(node) is None
 
 
 def test_simpletype_xmlvalue():
-    item = types.SimpleType()
+    item = types.AnySimpleType()
 
     with pytest.raises(NotImplementedError):
         item.xmlvalue(None)
 
 
 def test_simpletype_pythonvalue():
-    item = types.SimpleType()
+    item = types.AnySimpleType()
 
     with pytest.raises(NotImplementedError):
         item.pythonvalue(None)
 
 
 def test_simpletype_call_wrong_arg_count():
-    item = types.SimpleType()
+    item = types.AnySimpleType()
 
     with pytest.raises(TypeError):
         item('foo', 'bar')
 
 
 def test_simpletype_call_wrong_kwarg():
-    item = types.SimpleType()
+    item = types.AnySimpleType()
 
     with pytest.raises(TypeError):
         item(uhhh='x')
 
 
 def test_simpletype_str():
-    item = types.SimpleType()
+    item = types.AnySimpleType()
     item.name = u'foobar'
-    assert six.text_type(item) == 'SimpleType(value)'
+    assert six.text_type(item) == 'AnySimpleType(value)'
 
 
 def test_complextype_parse_xmlelement_no_childs():
diff --git a/tests/test_xsd_union.py b/tests/test_xsd_union.py
index ad56f93..1993ff5 100644
--- a/tests/test_xsd_union.py
+++ b/tests/test_xsd_union.py
@@ -1,9 +1,4 @@
-import datetime
-import io
-
-from lxml import etree
-
-from tests.utils import DummyTransport, assert_nodes_equal, load_xml, render_node
+from tests.utils import assert_nodes_equal, load_xml, render_node
 from zeep import xsd
 
 
diff --git a/tests/test_xsd_validation.py b/tests/test_xsd_validation.py
new file mode 100644
index 0000000..add6cf0
--- /dev/null
+++ b/tests/test_xsd_validation.py
@@ -0,0 +1,85 @@
+import pytest
+
+from tests.utils import assert_nodes_equal, load_xml, render_node
+from zeep import exceptions, xsd
+
+
+def test_validate_element_value():
+    schema = xsd.Schema(load_xml("""
+        <?xml version="1.0"?>
+        <schema xmlns="http://www.w3.org/2001/XMLSchema"
+                xmlns:tns="http://tests.python-zeep.org/"
+                targetNamespace="http://tests.python-zeep.org/"
+                elementFormDefault="qualified">
+          <element name="container">
+            <complexType>
+              <sequence>
+                <element minOccurs="1" maxOccurs="1" name="item" type="string" />
+              </sequence>
+            </complexType>
+          </element>
+        </schema>
+    """))
+    schema.set_ns_prefix('tns', 'http://tests.python-zeep.org/')
+
+    container_elm = schema.get_element('tns:container')
+    obj = container_elm()
+
+    expected = """
+      <document>
+        <ns0:container xmlns:ns0="http://tests.python-zeep.org/">
+          <ns0:item>bar</ns0:item>
+        </ns0:container>
+      </document>
+    """
+
+    with pytest.raises(exceptions.ValidationError) as exc:
+        result = render_node(container_elm, obj)
+    assert 'Missing element item (container.item)' in str(exc)
+
+    obj.item = 'bar'
+    result = render_node(container_elm, obj)
+
+    assert_nodes_equal(result, expected)
+
+    obj = container_elm.parse(result[0], schema)
+    assert obj.item == 'bar'
+
+
+def test_validate_required_attribute():
+    schema = xsd.Schema(load_xml("""
+        <?xml version="1.0"?>
+        <schema xmlns="http://www.w3.org/2001/XMLSchema"
+                xmlns:tns="http://tests.python-zeep.org/"
+                targetNamespace="http://tests.python-zeep.org/"
+                elementFormDefault="qualified">
+          <element name="container">
+            <complexType>
+              <attribute name="item" type="string" use="required"/>
+            </complexType>
+          </element>
+        </schema>
+    """))
+    schema.set_ns_prefix('tns', 'http://tests.python-zeep.org/')
+
+    container_elm = schema.get_element('tns:container')
+    obj = container_elm()
+
+    expected = """
+      <document>
+        <ns0:container xmlns:ns0="http://tests.python-zeep.org/" item="bar"/>
+      </document>
+    """
+
+    with pytest.raises(exceptions.ValidationError) as exc:
+        result = render_node(container_elm, obj)
+    assert 'The attribute item is not valid: Value is required (container.item)' in str(exc)
+
+
+    obj.item = 'bar'
+    result = render_node(container_elm, obj)
+
+    assert_nodes_equal(result, expected)
+
+    obj = container_elm.parse(result[0], schema)
+    assert obj.item == 'bar'
diff --git a/tests/test_xsd_visitor.py b/tests/test_xsd_visitor.py
index e731e08..66d561b 100644
--- a/tests/test_xsd_visitor.py
+++ b/tests/test_xsd_visitor.py
@@ -1,19 +1,16 @@
+import pytest
 from lxml import etree
 
 from tests.utils import assert_nodes_equal, load_xml, render_node
-from zeep import xsd
-from zeep.xsd import builtins
-from zeep.xsd.context import ParserContext
+from zeep import exceptions, xsd
 from zeep.xsd.schema import Schema
 
 
 def parse_schema_node(node):
-    parser_context = ParserContext()
     schema = Schema(
         node=node,
         transport=None,
-        location=None,
-        parser_context=parser_context)
+        location=None)
     return schema
 
 
@@ -26,7 +23,7 @@ def test_schema_empty():
         </schema>
     """)
     schema = parse_schema_node(node)
-    root = list(schema._schemas.values())[0]
+    root = next(schema.documents)
     assert root._element_form == 'qualified'
     assert root._attribute_form == 'unqualified'
 
@@ -70,7 +67,7 @@ def test_element_default_type():
     """)
     schema = parse_schema_node(node)
     element = schema.get_element('{http://tests.python-zeep.org/}foo')
-    assert isinstance(element.type, builtins.AnyType)
+    assert isinstance(element.type, xsd.AnyType)
 
 
 def test_element_simple_type_unresolved():
@@ -185,10 +182,15 @@ def test_attribute_required():
     xsd_element = schema.get_element('{http://tests.python-zeep.org/}foo')
     value = xsd_element()
 
+    with pytest.raises(exceptions.ValidationError):
+        node = render_node(xsd_element, value)
+
+    value.base = 'foo'
     node = render_node(xsd_element, value)
+
     expected = """
       <document>
-        <ns0:foo xmlns:ns0="http://tests.python-zeep.org/" base=""/>
+        <ns0:foo xmlns:ns0="http://tests.python-zeep.org/" base="foo"/>
       </document>
     """
     assert_nodes_equal(expected, node)
diff --git a/tests/utils.py b/tests/utils.py
index b0e8a16..2211831 100644
--- a/tests/utils.py
+++ b/tests/utils.py
@@ -4,7 +4,8 @@ from six import binary_type, string_types
 
 
 def load_xml(xml):
-    parser = etree.XMLParser(remove_blank_text=True, remove_comments=True)
+    parser = etree.XMLParser(
+        remove_blank_text=True, remove_comments=True, resolve_entities=False)
     return etree.fromstring(xml.strip(), parser=parser)
 
 
diff --git a/tests/wsdl_files/claim.wsdl b/tests/wsdl_files/claim.wsdl
new file mode 100644
index 0000000..923d14b
--- /dev/null
+++ b/tests/wsdl_files/claim.wsdl
@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<definitions
+    name="ClaimService"
+    targetNamespace="http://risky-stuff.com/soap/GetClaimDetails/"
+    xmlns="http://schemas.xmlsoap.org/wsdl/"
+    xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
+    xmlns:tns="http://risky-stuff.com/soap/GetClaimDetails/"
+    xmlns:xsd="http://www.w3.org/2001/XMLSchema">
+    <types>
+        <schema elementFormDefault="qualified"
+            targetNamespace="http://risky-stuff.com/soap/GetClaimDetails/"
+            xmlns="http://www.w3.org/2001/XMLSchema"
+            xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/"
+            xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/">
+            <simpleType name="ClaimID">
+                <restriction base="string"/>
+            </simpleType>
+        </schema>
+    </types>
+    <message name="GetClaimDetailsInput">
+    <part name="ClaimID" type="tns:ClaimID"/>
+    </message>
+    <message name="GetClaimDetailsOutput"/>
+    <portType name="GetClaimDetailsPortType">
+        <operation name="GetClaimDetails">
+            <input message="tns:GetClaimDetailsInput"/>
+            <output message="tns:GetClaimDetailsOutput"/>
+        </operation>
+    </portType>
+    <binding name="GetClaimDetailsBinding" type="tns:GetClaimDetailsPortType">
+        <soap:binding style="rpc" transport="http://schemas.xmlsoap.org/soap/http"/>
+        <operation name="GetClaimDetails">
+            <soap:operation soapAction="http://schemas.risk-stuff.com/soap/action/#GetClaimDetails"/>
+                <input>
+                    <soap:body encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" namespace="http://schemas.risk-stuff.com/soap/" use="encoded"/>
+                </input>
+                <output>
+                    <soap:body encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" namespace="http://schemas.risk-stuff.com/soap/" use="encoded"/>
+                </output>
+        </operation>
+    </binding>
+    <service name="GetClaimDetailsService">
+        <port binding="tns:GetClaimDetailsBinding" name="GetClaimDetailsPort">
+            <soap:address location="https://www.risky-stuff.com/claim.svc"/>
+        </port>
+    </service>
+</definitions>
diff --git a/tests/wsdl_files/soap.wsdl b/tests/wsdl_files/soap.wsdl
index 6cc923c..ab28e67 100644
--- a/tests/wsdl_files/soap.wsdl
+++ b/tests/wsdl_files/soap.wsdl
@@ -36,7 +36,7 @@
           <all>
             <element name="tickerSymbol" type="string"/>
             <element name="account" type="tns:account" minOccurs="0" />
-            <element ref="tns:country"/>
+            <element ref="tns:country" minOccurs="0"/>
           </all>
         </complexType>
       </element>
diff --git a/tests/wsdl_files/soap_header.wsdl b/tests/wsdl_files/soap_header.wsdl
index b3f6c6b..04e1269 100644
--- a/tests/wsdl_files/soap_header.wsdl
+++ b/tests/wsdl_files/soap_header.wsdl
@@ -31,10 +31,18 @@
           </sequence>
         </complexType>
       </element>
+      <element name="Token">
+        <complexType>
+          <sequence>
+            <element name="value" type="string"/>
+          </sequence>
+        </complexType>
+      </element>
     </schema>
   </types>
   <message name="GetLastTradePriceInput">
     <part name="header" element="xsd1:Authentication"/>
+    <part name="token" element="xsd1:Token"/>
     <part name="body" element="xsd1:TradePriceRequest"/>
   </message>
   <message name="GetLastTradePriceOutput">
@@ -52,6 +60,7 @@
       <soap:operation soapAction="http://example.com/GetLastTradePrice"/>
       <input>
         <soap:header message="tns:GetLastTradePriceInput" part="header" use="literal"/>
+        <soap:header message="tns:GetLastTradePriceInput" part="token" use="literal"/>
         <soap:body use="literal"/>
       </input>
       <output>
commit fd71ad4720c31105623b8f93165cae8912cbaac5
Author: Mathias Behrle <mathiasb at m9s.biz>
Date:   Tue Jan 31 12:34:05 2017 +0100

    Put a minimal version constraint for python-six.
    
    This is needed for backports to pull from jessie-backports instead of jessie.

diff --git a/debian/control b/debian/control
index 5163435..99cbd52 100644
--- a/debian/control
+++ b/debian/control
@@ -23,7 +23,7 @@ Build-Depends:
  python-requests,
  python-requests-mock,
  python-setuptools,
- python-six,
+ python-six (>= 1.9.0-3~),
  python-tz,
  python3,
  python3-appdirs,
@@ -60,7 +60,7 @@ Depends:
  python-pkg-resources,
  python-requests,
  python-setuptools,
- python-six,
+ python-six (>= 1.9.0-3~),
  python-tz,
  ${misc:Depends},
  ${python:Depends},
commit 6287da25cb2c9aadc9e0905c50633e23a88680bf
Author: Mathias Behrle <mathiasb at m9s.biz>
Date:   Tue Jan 31 12:32:30 2017 +0100

    Remove 01-add-missing-xsd-files.patch, went upstream.

diff --git a/debian/patches/01-add-missing-xsd-files.patch b/debian/patches/01-add-missing-xsd-files.patch
deleted file mode 100644
index c615cae..0000000
--- a/debian/patches/01-add-missing-xsd-files.patch
+++ /dev/null
@@ -1,1007 +0,0 @@
-Description: Add missing test files
- Those files needed to run the tests are in the git repository,
- but not in the tarball on pypi.
-Author: Mathias Behrle <mbehrle at debian.org>
-Bug-Debian: https://bugs.debian.org/834485
-Bug: https://github.com/mvantellingen/python-zeep/issues/27
-Last-Update: 2016-12-06
-
---- /dev/null	1970-01-01 00:00:00.000000000 +0000
-+++ python-zeep/tests/wsdl_files/soap-enc.xsd	2016-12-06 18:34:15.926748461 +0100
-@@ -0,0 +1,535 @@
-+<?xml version='1.0' encoding='UTF-8' ?>
-+
-+<!-- Schema for the SOAP/1.1 encoding
-+
-+Portions © 2001 DevelopMentor. 
-+© 2001 W3C (Massachusetts Institute of Technology, Institut National de Recherche en Informatique et en Automatique, Keio University). All Rights Reserved.  
-+ 
-+This document is governed by the W3C Software License [1] as described in the FAQ [2].
-+[1] http://www.w3.org/Consortium/Legal/copyright-software-19980720
-+[2] http://www.w3.org/Consortium/Legal/IPR-FAQ-20000620.html#DTD 
-+By obtaining, using and/or copying this work, you (the licensee) agree that you have read, understood, and will comply with the following terms and conditions:
-+
-+Permission to use, copy, modify, and distribute this software and its documentation, with or without modification,  for any purpose and without fee or royalty is hereby granted, provided that you include the following on ALL copies of the software and documentation or portions thereof, including modifications, that you make:
-+
-+1.  The full text of this NOTICE in a location viewable to users of the redistributed or derivative work. 
-+
-+2.  Any pre-existing intellectual property disclaimers, notices, or terms and conditions. If none exist, a short notice of the following form (hypertext is preferred, text is permitted) should be used within the body of any redistributed or derivative code: "Copyright © 2001 World Wide Web Consortium, (Massachusetts Institute of Technology, Institut National de Recherche en Informatique et en Automatique, Keio University). All Rights Reserved. http://www.w3.org/Consortium/Legal/" 
-+
-+3.  Notice of any changes or modifications to the W3C files, including the date changes were made. (We recommend you provide URIs to the location from which the code is derived.)   
-+
-+Original W3C files; http://www.w3.org/2001/06/soap-encoding
-+Changes made: 
-+     - reverted namespace to http://schemas.xmlsoap.org/soap/encoding/
-+     - reverted root to only allow 0 and 1 as lexical values
-+	 - removed default value from root attribute declaration
-+
-+THIS SOFTWARE AND DOCUMENTATION IS PROVIDED "AS IS," AND COPYRIGHT HOLDERS MAKE NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO, WARRANTIES OF MERCHANTABILITY OR FITNESS FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF THE SOFTWARE OR DOCUMENTATION WILL NOT INFRINGE ANY THIRD PARTY PATENTS, COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS.
-+
-+COPYRIGHT HOLDERS WILL NOT BE LIABLE FOR ANY DIRECT, INDIRECT, SPECIAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF ANY USE OF THE SOFTWARE OR DOCUMENTATION.
-+
-+The name and trademarks of copyright holders may NOT be used in advertising or publicity pertaining to the software without specific, written prior permission. Title to copyright in this software and any associated documentation will at all times remain with copyright holders.
-+
-+-->
-+<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
-+           xmlns:tns="http://schemas.xmlsoap.org/soap/encoding/"
-+           targetNamespace="http://schemas.xmlsoap.org/soap/encoding/" >
-+        
-+ <xs:attribute name="root" >
-+   <xs:annotation>
-+     <xs:documentation>
-+	   'root' can be used to distinguish serialization roots from other
-+       elements that are present in a serialization but are not roots of
-+       a serialized value graph 
-+	 </xs:documentation>
-+   </xs:annotation>
-+   <xs:simpleType>
-+     <xs:restriction base='xs:boolean'>
-+	   <xs:pattern value='0|1' />
-+	 </xs:restriction>
-+   </xs:simpleType>
-+ </xs:attribute>
-+
-+  <xs:attributeGroup name="commonAttributes" >
-+    <xs:annotation>
-+	  <xs:documentation>
-+	    Attributes common to all elements that function as accessors or 
-+        represent independent (multi-ref) values.  The href attribute is
-+        intended to be used in a manner like CONREF.  That is, the element
-+        content should be empty iff the href attribute appears
-+	  </xs:documentation>
-+	</xs:annotation>
-+    <xs:attribute name="id" type="xs:ID" />
-+    <xs:attribute name="href" type="xs:anyURI" />
-+    <xs:anyAttribute namespace="##other" processContents="lax" />
-+  </xs:attributeGroup>
-+
-+  <!-- Global Attributes.  The following attributes are intended to be usable via qualified attribute names on any complex type referencing them. -->
-+       
-+  <!-- Array attributes. Needed to give the type and dimensions of an array's contents, and the offset for partially-transmitted arrays. -->
-+   
-+  <xs:simpleType name="arrayCoordinate" >
-+    <xs:restriction base="xs:string" />
-+  </xs:simpleType>
-+          
-+  <xs:attribute name="arrayType" type="xs:string" />
-+  <xs:attribute name="offset" type="tns:arrayCoordinate" />
-+  
-+  <xs:attributeGroup name="arrayAttributes" >
-+    <xs:attribute ref="tns:arrayType" />
-+    <xs:attribute ref="tns:offset" />
-+  </xs:attributeGroup>    
-+  
-+  <xs:attribute name="position" type="tns:arrayCoordinate" /> 
-+  
-+  <xs:attributeGroup name="arrayMemberAttributes" >
-+    <xs:attribute ref="tns:position" />
-+  </xs:attributeGroup>    
-+
-+  <xs:group name="Array" >
-+    <xs:sequence>
-+      <xs:any namespace="##any" minOccurs="0" maxOccurs="unbounded" processContents="lax" />
-+	</xs:sequence>
-+  </xs:group>
-+
-+  <xs:element name="Array" type="tns:Array" />
-+  <xs:complexType name="Array" >
-+    <xs:annotation>
-+	  <xs:documentation>
-+	   'Array' is a complex type for accessors identified by position 
-+	  </xs:documentation>
-+	</xs:annotation>
-+    <xs:group ref="tns:Array" minOccurs="0" />
-+    <xs:attributeGroup ref="tns:arrayAttributes" />
-+    <xs:attributeGroup ref="tns:commonAttributes" />
-+  </xs:complexType> 
-+
-+  <!-- 'Struct' is a complex type for accessors identified by name. 
-+       Constraint: No element may be have the same name as any other,
-+       nor may any element have a maxOccurs > 1. -->
-+   
-+  <xs:element name="Struct" type="tns:Struct" />
-+
-+  <xs:group name="Struct" >
-+    <xs:sequence>
-+      <xs:any namespace="##any" minOccurs="0" maxOccurs="unbounded" processContents="lax" />
-+	</xs:sequence>
-+  </xs:group>
-+
-+  <xs:complexType name="Struct" >
-+    <xs:group ref="tns:Struct" minOccurs="0" />
-+    <xs:attributeGroup ref="tns:commonAttributes"/>
-+  </xs:complexType> 
-+
-+  <!-- 'Base64' can be used to serialize binary data using base64 encoding
-+       as defined in RFC2045 but without the MIME line length limitation. -->
-+
-+  <xs:simpleType name="base64" >
-+    <xs:restriction base="xs:base64Binary" />
-+  </xs:simpleType>
-+
-+ <!-- Element declarations corresponding to each of the simple types in the 
-+      XML Schemas Specification. -->
-+
-+  <xs:element name="duration" type="tns:duration" />
-+  <xs:complexType name="duration" >
-+    <xs:simpleContent>
-+      <xs:extension base="xs:duration" >
-+        <xs:attributeGroup ref="tns:commonAttributes" />
-+      </xs:extension>
-+    </xs:simpleContent>
-+  </xs:complexType>
-+
-+  <xs:element name="dateTime" type="tns:dateTime" />
-+  <xs:complexType name="dateTime" >
-+    <xs:simpleContent>
-+      <xs:extension base="xs:dateTime" >
-+        <xs:attributeGroup ref="tns:commonAttributes" />
-+      </xs:extension>
-+    </xs:simpleContent>
-+  </xs:complexType>
-+
-+
-+
-+  <xs:element name="NOTATION" type="tns:NOTATION" />
-+  <xs:complexType name="NOTATION" >
-+    <xs:simpleContent>
-+      <xs:extension base="xs:QName" >
-+        <xs:attributeGroup ref="tns:commonAttributes" />
-+      </xs:extension>
-+    </xs:simpleContent>
-+  </xs:complexType>
-+  
-+
-+  <xs:element name="time" type="tns:time" />
-+  <xs:complexType name="time" >
-+    <xs:simpleContent>
-+      <xs:extension base="xs:time" >
-+        <xs:attributeGroup ref="tns:commonAttributes" />
-+      </xs:extension>
-+    </xs:simpleContent>
-+  </xs:complexType>
-+
-+  <xs:element name="date" type="tns:date" />
-+  <xs:complexType name="date" >
-+    <xs:simpleContent>
-+      <xs:extension base="xs:date" >
-+        <xs:attributeGroup ref="tns:commonAttributes" />
-+      </xs:extension>
-+    </xs:simpleContent>
-+  </xs:complexType>
-+
-+  <xs:element name="gYearMonth" type="tns:gYearMonth" />
-+  <xs:complexType name="gYearMonth" >
-+    <xs:simpleContent>
-+      <xs:extension base="xs:gYearMonth" >
-+        <xs:attributeGroup ref="tns:commonAttributes" />
-+      </xs:extension>
-+    </xs:simpleContent>
-+  </xs:complexType>
-+
-+  <xs:element name="gYear" type="tns:gYear" />
-+  <xs:complexType name="gYear" >
-+    <xs:simpleContent>
-+      <xs:extension base="xs:gYear" >
-+        <xs:attributeGroup ref="tns:commonAttributes" />
-+      </xs:extension>
-+    </xs:simpleContent>
-+  </xs:complexType>
-+
-+  <xs:element name="gMonthDay" type="tns:gMonthDay" />
-+  <xs:complexType name="gMonthDay" >
-+    <xs:simpleContent>
-+      <xs:extension base="xs:gMonthDay" >
-+        <xs:attributeGroup ref="tns:commonAttributes" />
-+      </xs:extension>
-+    </xs:simpleContent>
-+  </xs:complexType>
-+
-+  <xs:element name="gDay" type="tns:gDay" />
-+  <xs:complexType name="gDay" >
-+    <xs:simpleContent>
-+      <xs:extension base="xs:gDay" >
-+        <xs:attributeGroup ref="tns:commonAttributes" />
-+      </xs:extension>
-+    </xs:simpleContent>
-+  </xs:complexType>
-+
-+  <xs:element name="gMonth" type="tns:gMonth" />
-+  <xs:complexType name="gMonth" >
-+    <xs:simpleContent>
-+      <xs:extension base="xs:gMonth" >
-+        <xs:attributeGroup ref="tns:commonAttributes" />
-+      </xs:extension>
-+    </xs:simpleContent>
-+  </xs:complexType>
-+  
-+  <xs:element name="boolean" type="tns:boolean" />
-+  <xs:complexType name="boolean" >
-+    <xs:simpleContent>
-+      <xs:extension base="xs:boolean" >
-+        <xs:attributeGroup ref="tns:commonAttributes" />
-+      </xs:extension>
-+    </xs:simpleContent>
-+  </xs:complexType>
-+
-+  <xs:element name="base64Binary" type="tns:base64Binary" />
-+  <xs:complexType name="base64Binary" >
-+    <xs:simpleContent>
-+      <xs:extension base="xs:base64Binary" >
-+        <xs:attributeGroup ref="tns:commonAttributes" />
-+      </xs:extension>
-+    </xs:simpleContent>
-+  </xs:complexType>
-+
-+  <xs:element name="hexBinary" type="tns:hexBinary" />
-+  <xs:complexType name="hexBinary" >
-+    <xs:simpleContent>
-+     <xs:extension base="xs:hexBinary" >
-+       <xs:attributeGroup ref="tns:commonAttributes" />
-+     </xs:extension>
-+    </xs:simpleContent>
-+  </xs:complexType>
-+
-+  <xs:element name="float" type="tns:float" />
-+  <xs:complexType name="float" >
-+    <xs:simpleContent>
-+      <xs:extension base="xs:float" >
-+        <xs:attributeGroup ref="tns:commonAttributes" />
-+      </xs:extension>
-+    </xs:simpleContent>
-+  </xs:complexType>
-+
-+  <xs:element name="double" type="tns:double" />
-+  <xs:complexType name="double" >
-+    <xs:simpleContent>
-+      <xs:extension base="xs:double" >
-+        <xs:attributeGroup ref="tns:commonAttributes" />
-+      </xs:extension>
-+    </xs:simpleContent>
-+  </xs:complexType>
-+
-+  <xs:element name="anyURI" type="tns:anyURI" />
-+  <xs:complexType name="anyURI" >
-+    <xs:simpleContent>
-+      <xs:extension base="xs:anyURI" >
-+        <xs:attributeGroup ref="tns:commonAttributes" />
-+      </xs:extension>
-+    </xs:simpleContent>
-+  </xs:complexType>
-+
-+  <xs:element name="QName" type="tns:QName" />
-+  <xs:complexType name="QName" >
-+    <xs:simpleContent>
-+      <xs:extension base="xs:QName" >
-+        <xs:attributeGroup ref="tns:commonAttributes" />
-+      </xs:extension>
-+    </xs:simpleContent>
-+  </xs:complexType>
-+
-+  
-+  <xs:element name="string" type="tns:string" />
-+  <xs:complexType name="string" >
-+    <xs:simpleContent>
-+      <xs:extension base="xs:string" >
-+        <xs:attributeGroup ref="tns:commonAttributes" />
-+      </xs:extension>
-+    </xs:simpleContent>
-+  </xs:complexType>
-+
-+  <xs:element name="normalizedString" type="tns:normalizedString" />
-+  <xs:complexType name="normalizedString" >
-+    <xs:simpleContent>
-+      <xs:extension base="xs:normalizedString" >
-+        <xs:attributeGroup ref="tns:commonAttributes" />
-+      </xs:extension>
-+    </xs:simpleContent>
-+  </xs:complexType>
-+
-+  <xs:element name="token" type="tns:token" />
-+  <xs:complexType name="token" >
-+    <xs:simpleContent>
-+      <xs:extension base="xs:token" >
-+        <xs:attributeGroup ref="tns:commonAttributes" />
-+      </xs:extension>
-+    </xs:simpleContent>
-+  </xs:complexType>
-+
-+  <xs:element name="language" type="tns:language" />
-+  <xs:complexType name="language" >
-+    <xs:simpleContent>
-+      <xs:extension base="xs:language" >
-+        <xs:attributeGroup ref="tns:commonAttributes" />
-+      </xs:extension>
-+    </xs:simpleContent>
-+  </xs:complexType>
-+
-+  <xs:element name="Name" type="tns:Name" />
-+  <xs:complexType name="Name" >
-+    <xs:simpleContent>
-+      <xs:extension base="xs:Name" >
-+        <xs:attributeGroup ref="tns:commonAttributes" />
-+      </xs:extension>
-+    </xs:simpleContent>
-+  </xs:complexType>
-+
-+  <xs:element name="NMTOKEN" type="tns:NMTOKEN" />
-+  <xs:complexType name="NMTOKEN" >
-+    <xs:simpleContent>
-+      <xs:extension base="xs:NMTOKEN" >
-+        <xs:attributeGroup ref="tns:commonAttributes" />
-+      </xs:extension>
-+    </xs:simpleContent>
-+  </xs:complexType>
-+
-+  <xs:element name="NCName" type="tns:NCName" />
-+  <xs:complexType name="NCName" >
-+    <xs:simpleContent>
-+      <xs:extension base="xs:NCName" >
-+        <xs:attributeGroup ref="tns:commonAttributes" />
-+      </xs:extension>
-+    </xs:simpleContent>
-+  </xs:complexType>
-+
-+  <xs:element name="NMTOKENS" type="tns:NMTOKENS" />
-+  <xs:complexType name="NMTOKENS" >
-+    <xs:simpleContent>
-+      <xs:extension base="xs:NMTOKENS" >
-+        <xs:attributeGroup ref="tns:commonAttributes" />
-+      </xs:extension>
-+    </xs:simpleContent>
-+  </xs:complexType>
-+
-+  <xs:element name="ID" type="tns:ID" />
-+  <xs:complexType name="ID" >
-+    <xs:simpleContent>
-+      <xs:extension base="xs:ID" >
-+        <xs:attributeGroup ref="tns:commonAttributes" />
-+      </xs:extension>
-+    </xs:simpleContent>
-+  </xs:complexType>
-+
-+  <xs:element name="IDREF" type="tns:IDREF" />
-+  <xs:complexType name="IDREF" >
-+    <xs:simpleContent>
-+      <xs:extension base="xs:IDREF" >
-+        <xs:attributeGroup ref="tns:commonAttributes" />
-+      </xs:extension>
-+    </xs:simpleContent>
-+  </xs:complexType>
-+
-+  <xs:element name="ENTITY" type="tns:ENTITY" />
-+  <xs:complexType name="ENTITY" >
-+    <xs:simpleContent>
-+      <xs:extension base="xs:ENTITY" >
-+        <xs:attributeGroup ref="tns:commonAttributes" />
-+      </xs:extension>
-+    </xs:simpleContent>
-+  </xs:complexType>
-+
-+  <xs:element name="IDREFS" type="tns:IDREFS" />
-+  <xs:complexType name="IDREFS" >
-+    <xs:simpleContent>
-+      <xs:extension base="xs:IDREFS" >
-+        <xs:attributeGroup ref="tns:commonAttributes" />
-+      </xs:extension>
-+    </xs:simpleContent>
-+  </xs:complexType>
-+
-+  <xs:element name="ENTITIES" type="tns:ENTITIES" />
-+  <xs:complexType name="ENTITIES" >
-+    <xs:simpleContent>
-+      <xs:extension base="xs:ENTITIES" >
-+        <xs:attributeGroup ref="tns:commonAttributes" />
-+      </xs:extension>
-+    </xs:simpleContent>
-+  </xs:complexType>
-+
-+  <xs:element name="decimal" type="tns:decimal" />
-+  <xs:complexType name="decimal" >
-+    <xs:simpleContent>
-+      <xs:extension base="xs:decimal" >
-+        <xs:attributeGroup ref="tns:commonAttributes" />
-+      </xs:extension>
-+    </xs:simpleContent>
-+  </xs:complexType>
-+
-+  <xs:element name="integer" type="tns:integer" />
-+  <xs:complexType name="integer" >
-+    <xs:simpleContent>
-+      <xs:extension base="xs:integer" >
-+        <xs:attributeGroup ref="tns:commonAttributes" />
-+      </xs:extension>
-+    </xs:simpleContent>
-+  </xs:complexType>
-+
-+  <xs:element name="nonPositiveInteger" type="tns:nonPositiveInteger" />
-+  <xs:complexType name="nonPositiveInteger" >
-+    <xs:simpleContent>
-+      <xs:extension base="xs:nonPositiveInteger" >
-+        <xs:attributeGroup ref="tns:commonAttributes" />
-+      </xs:extension>
-+    </xs:simpleContent>
-+  </xs:complexType>
-+
-+  <xs:element name="negativeInteger" type="tns:negativeInteger" />
-+  <xs:complexType name="negativeInteger" >
-+    <xs:simpleContent>
-+      <xs:extension base="xs:negativeInteger" >
-+        <xs:attributeGroup ref="tns:commonAttributes" />
-+      </xs:extension>
-+    </xs:simpleContent>
-+  </xs:complexType>
-+
-+  <xs:element name="long" type="tns:long" />
-+  <xs:complexType name="long" >
-+    <xs:simpleContent>
-+      <xs:extension base="xs:long" >
-+        <xs:attributeGroup ref="tns:commonAttributes" />
-+      </xs:extension>
-+    </xs:simpleContent>
-+  </xs:complexType>
-+
-+  <xs:element name="int" type="tns:int" />
-+  <xs:complexType name="int" >
-+    <xs:simpleContent>
-+      <xs:extension base="xs:int" >
-+        <xs:attributeGroup ref="tns:commonAttributes" />
-+      </xs:extension>
-+    </xs:simpleContent>
-+  </xs:complexType>
-+
-+  <xs:element name="short" type="tns:short" />
-+  <xs:complexType name="short" >
-+    <xs:simpleContent>
-+      <xs:extension base="xs:short" >
-+        <xs:attributeGroup ref="tns:commonAttributes" />
-+      </xs:extension>
-+    </xs:simpleContent>
-+  </xs:complexType>
-+
-+  <xs:element name="byte" type="tns:byte" />
-+  <xs:complexType name="byte" >
-+    <xs:simpleContent>
-+      <xs:extension base="xs:byte" >
-+        <xs:attributeGroup ref="tns:commonAttributes" />
-+      </xs:extension>
-+    </xs:simpleContent>
-+  </xs:complexType>
-+
-+  <xs:element name="nonNegativeInteger" type="tns:nonNegativeInteger" />
-+  <xs:complexType name="nonNegativeInteger" >
-+    <xs:simpleContent>
-+      <xs:extension base="xs:nonNegativeInteger" >
-+        <xs:attributeGroup ref="tns:commonAttributes" />
-+      </xs:extension>
-+    </xs:simpleContent>
-+  </xs:complexType>
-+
-+  <xs:element name="unsignedLong" type="tns:unsignedLong" />
-+  <xs:complexType name="unsignedLong" >
-+    <xs:simpleContent>
-+      <xs:extension base="xs:unsignedLong" >
-+        <xs:attributeGroup ref="tns:commonAttributes" />
-+      </xs:extension>
-+    </xs:simpleContent>
-+  </xs:complexType>
-+
-+  <xs:element name="unsignedInt" type="tns:unsignedInt" />
-+  <xs:complexType name="unsignedInt" >
-+    <xs:simpleContent>
-+      <xs:extension base="xs:unsignedInt" >
-+        <xs:attributeGroup ref="tns:commonAttributes" />
-+      </xs:extension>
-+    </xs:simpleContent>
-+  </xs:complexType>
-+
-+  <xs:element name="unsignedShort" type="tns:unsignedShort" />
-+  <xs:complexType name="unsignedShort" >
-+    <xs:simpleContent>
-+      <xs:extension base="xs:unsignedShort" >
-+        <xs:attributeGroup ref="tns:commonAttributes" />
-+      </xs:extension>
-+    </xs:simpleContent>
-+  </xs:complexType>
-+
-+  <xs:element name="unsignedByte" type="tns:unsignedByte" />
-+  <xs:complexType name="unsignedByte" >
-+    <xs:simpleContent>
-+      <xs:extension base="xs:unsignedByte" >
-+        <xs:attributeGroup ref="tns:commonAttributes" />
-+      </xs:extension>
-+    </xs:simpleContent>
-+  </xs:complexType>
-+
-+  <xs:element name="positiveInteger" type="tns:positiveInteger" />
-+  <xs:complexType name="positiveInteger" >
-+    <xs:simpleContent>
-+      <xs:extension base="xs:positiveInteger" >
-+        <xs:attributeGroup ref="tns:commonAttributes" />
-+      </xs:extension>
-+    </xs:simpleContent>
-+  </xs:complexType>
-+
-+  <xs:element name="anyType" />
-+</xs:schema>
---- /dev/null	1970-01-01 00:00:00.000000000 +0000
-+++ python-zeep/tests/wsdl_files/test_import_2.xsd	2016-12-06 18:34:15.926748461 +0100
-@@ -0,0 +1,60 @@
-+<schema xmlns="http://www.w3.org/2001/XMLSchema" 
-+        targetNamespace="http://example.com/stockquote.xsd"
-+        xmlns:tns="http://example.com/stockquote.xsd" >
-+  <complexType name="Address">
-+    <sequence>
-+      <element minOccurs="0" maxOccurs="1" name="NameFirst" type="string"/>
-+      <element minOccurs="0" maxOccurs="1" name="NameLast" type="string"/>
-+      <element minOccurs="0" maxOccurs="1" name="Email" type="string"/>
-+    </sequence>
-+  </complexType>
-+  <element name="Fault1">
-+    <complexType>
-+      <sequence>
-+        <element name="message" type="string"/>
-+      </sequence>
-+    </complexType>
-+  </element>
-+  <element name="Fault2">
-+    <complexType>
-+      <sequence>
-+        <element name="message" type="string"/>
-+      </sequence>
-+    </complexType>
-+  </element>
-+  <element name="TradePriceRequest">
-+    <complexType>
-+      <all>
-+        <element name="tickerSymbol" type="string"/>
-+        <element name="account" type="tns:account" minOccurs="0" />
-+        <element ref="tns:country"/>
-+      </all>
-+    </complexType>
-+  </element>
-+  <element name="TradePrice">
-+    <complexType>
-+      <all>
-+        <element name="price" type="float"/>
-+      </all>
-+    </complexType>
-+  </element>
-+  <complexType name="account">
-+    <sequence>
-+      <element name="id" type="int"/>
-+      <element name="user" type="string"/>
-+    </sequence>
-+  </complexType>
-+  <complexType name="country">
-+      <sequence>
-+        <element name="code" type="string"/>
-+      </sequence>
-+  </complexType>
-+  <element name="country">
-+    <complexType>
-+      <sequence>
-+        <element name="name" type="string"/>
-+        <element name="code" type="string"/>
-+      </sequence>
-+    </complexType>
-+  </element>
-+</schema>
---- /dev/null	1970-01-01 00:00:00.000000000 +0000
-+++ python-zeep/tests/wsdl_files/xmldsig-core-schema.xsd	2016-12-06 18:34:15.926748461 +0100
-@@ -0,0 +1,309 @@
-+<?xml version="1.0" encoding="utf-8"?>
-+
-+<!-- Schema for XML Signatures
-+    http://www.w3.org/2000/09/xmldsig#
-+    $Revision: 1.1 $ on $Date: 2002/02/08 20:32:26 $ by $Author: reagle $
-+
-+    Copyright 2001 The Internet Society and W3C (Massachusetts Institute
-+    of Technology, Institut National de Recherche en Informatique et en
-+    Automatique, Keio University). All Rights Reserved.
-+    http://www.w3.org/Consortium/Legal/
-+
-+    This document is governed by the W3C Software License [1] as described
-+    in the FAQ [2].
-+
-+    [1] http://www.w3.org/Consortium/Legal/copyright-software-19980720
-+    [2] http://www.w3.org/Consortium/Legal/IPR-FAQ-20000620.html#DTD
-+-->
-+
-+
-+<schema xmlns="http://www.w3.org/2001/XMLSchema"
-+        xmlns:ds="http://www.w3.org/2000/09/xmldsig#"
-+        targetNamespace="http://www.w3.org/2000/09/xmldsig#"
-+        version="0.1" elementFormDefault="qualified"> 
-+
-+<!-- Basic Types Defined for Signatures -->
-+
-+<simpleType name="CryptoBinary">
-+  <restriction base="base64Binary">
-+  </restriction>
-+</simpleType>
-+
-+<!-- Start Signature -->
-+
-+<element name="Signature" type="ds:SignatureType"/>
-+<complexType name="SignatureType">
-+  <sequence> 
-+    <element ref="ds:SignedInfo"/> 
-+    <element ref="ds:SignatureValue"/> 
-+    <element ref="ds:KeyInfo" minOccurs="0"/> 
-+    <element ref="ds:Object" minOccurs="0" maxOccurs="unbounded"/> 
-+  </sequence>  
-+  <attribute name="Id" type="ID" use="optional"/>
-+</complexType>
-+
-+  <element name="SignatureValue" type="ds:SignatureValueType"/> 
-+  <complexType name="SignatureValueType">
-+    <simpleContent>
-+      <extension base="base64Binary">
-+        <attribute name="Id" type="ID" use="optional"/>
-+      </extension>
-+    </simpleContent>
-+  </complexType>
-+
-+<!-- Start SignedInfo -->
-+
-+<element name="SignedInfo" type="ds:SignedInfoType"/>
-+<complexType name="SignedInfoType">
-+  <sequence> 
-+    <element ref="ds:CanonicalizationMethod"/> 
-+    <element ref="ds:SignatureMethod"/> 
-+    <element ref="ds:Reference" maxOccurs="unbounded"/> 
-+  </sequence>  
-+  <attribute name="Id" type="ID" use="optional"/> 
-+</complexType>
-+
-+  <element name="CanonicalizationMethod" type="ds:CanonicalizationMethodType"/> 
-+  <complexType name="CanonicalizationMethodType" mixed="true">
-+    <sequence>
-+      <any namespace="##any" minOccurs="0" maxOccurs="unbounded"/>
-+      <!-- (0,unbounded) elements from (1,1) namespace -->
-+    </sequence>
-+    <attribute name="Algorithm" type="anyURI" use="required"/> 
-+  </complexType>
-+
-+  <element name="SignatureMethod" type="ds:SignatureMethodType"/>
-+  <complexType name="SignatureMethodType" mixed="true">
-+    <sequence>
-+      <element name="HMACOutputLength" minOccurs="0" type="ds:HMACOutputLengthType"/>
-+      <any namespace="##other" minOccurs="0" maxOccurs="unbounded"/>
-+      <!-- (0,unbounded) elements from (1,1) external namespace -->
-+    </sequence>
-+    <attribute name="Algorithm" type="anyURI" use="required"/> 
-+  </complexType>
-+
-+<!-- Start Reference -->
-+
-+<element name="Reference" type="ds:ReferenceType"/>
-+<complexType name="ReferenceType">
-+  <sequence> 
-+    <element ref="ds:Transforms" minOccurs="0"/> 
-+    <element ref="ds:DigestMethod"/> 
-+    <element ref="ds:DigestValue"/> 
-+  </sequence>
-+  <attribute name="Id" type="ID" use="optional"/> 
-+  <attribute name="URI" type="anyURI" use="optional"/> 
-+  <attribute name="Type" type="anyURI" use="optional"/> 
-+</complexType>
-+
-+  <element name="Transforms" type="ds:TransformsType"/>
-+  <complexType name="TransformsType">
-+    <sequence>
-+      <element ref="ds:Transform" maxOccurs="unbounded"/>  
-+    </sequence>
-+  </complexType>
-+
-+  <element name="Transform" type="ds:TransformType"/>
-+  <complexType name="TransformType" mixed="true">
-+    <choice minOccurs="0" maxOccurs="unbounded"> 
-+      <any namespace="##other" processContents="lax"/>
-+      <!-- (1,1) elements from (0,unbounded) namespaces -->
-+      <element name="XPath" type="string"/> 
-+    </choice>
-+    <attribute name="Algorithm" type="anyURI" use="required"/> 
-+  </complexType>
-+
-+<!-- End Reference -->
-+
-+<element name="DigestMethod" type="ds:DigestMethodType"/>
-+<complexType name="DigestMethodType" mixed="true"> 
-+  <sequence>
-+    <any namespace="##other" processContents="lax" minOccurs="0" maxOccurs="unbounded"/>
-+  </sequence>    
-+  <attribute name="Algorithm" type="anyURI" use="required"/> 
-+</complexType>
-+
-+<element name="DigestValue" type="ds:DigestValueType"/>
-+<simpleType name="DigestValueType">
-+  <restriction base="base64Binary"/>
-+</simpleType>
-+
-+<!-- End SignedInfo -->
-+
-+<!-- Start KeyInfo -->
-+
-+<element name="KeyInfo" type="ds:KeyInfoType"/> 
-+<complexType name="KeyInfoType" mixed="true">
-+  <choice maxOccurs="unbounded">     
-+    <element ref="ds:KeyName"/> 
-+    <element ref="ds:KeyValue"/> 
-+    <element ref="ds:RetrievalMethod"/> 
-+    <element ref="ds:X509Data"/> 
-+    <element ref="ds:PGPData"/> 
-+    <element ref="ds:SPKIData"/>
-+    <element ref="ds:MgmtData"/>
-+    <any processContents="lax" namespace="##other"/>
-+    <!-- (1,1) elements from (0,unbounded) namespaces -->
-+  </choice>
-+  <attribute name="Id" type="ID" use="optional"/> 
-+</complexType>
-+
-+  <element name="KeyName" type="string"/>
-+  <element name="MgmtData" type="string"/>
-+
-+  <element name="KeyValue" type="ds:KeyValueType"/> 
-+  <complexType name="KeyValueType" mixed="true">
-+   <choice>
-+     <element ref="ds:DSAKeyValue"/>
-+     <element ref="ds:RSAKeyValue"/>
-+     <any namespace="##other" processContents="lax"/>
-+   </choice>
-+  </complexType>
-+
-+  <element name="RetrievalMethod" type="ds:RetrievalMethodType"/> 
-+  <complexType name="RetrievalMethodType">
-+    <sequence>
-+      <element ref="ds:Transforms" minOccurs="0"/> 
-+    </sequence>  
-+    <attribute name="URI" type="anyURI"/>
-+    <attribute name="Type" type="anyURI" use="optional"/>
-+  </complexType>
-+
-+<!-- Start X509Data -->
-+
-+<element name="X509Data" type="ds:X509DataType"/> 
-+<complexType name="X509DataType">
-+  <sequence maxOccurs="unbounded">
-+    <choice>
-+      <element name="X509IssuerSerial" type="ds:X509IssuerSerialType"/>
-+      <element name="X509SKI" type="base64Binary"/>
-+      <element name="X509SubjectName" type="string"/>
-+      <element name="X509Certificate" type="base64Binary"/>
-+      <element name="X509CRL" type="base64Binary"/>
-+      <any namespace="##other" processContents="lax"/>
-+    </choice>
-+  </sequence>
-+</complexType>
-+
-+<complexType name="X509IssuerSerialType"> 
-+  <sequence> 
-+    <element name="X509IssuerName" type="string"/> 
-+    <element name="X509SerialNumber" type="integer"/> 
-+  </sequence>
-+</complexType>
-+
-+<!-- End X509Data -->
-+
-+<!-- Begin PGPData -->
-+
-+<element name="PGPData" type="ds:PGPDataType"/> 
-+<complexType name="PGPDataType"> 
-+  <choice>
-+    <sequence>
-+      <element name="PGPKeyID" type="base64Binary"/> 
-+      <element name="PGPKeyPacket" type="base64Binary" minOccurs="0"/> 
-+      <any namespace="##other" processContents="lax" minOccurs="0"
-+       maxOccurs="unbounded"/>
-+    </sequence>
-+    <sequence>
-+      <element name="PGPKeyPacket" type="base64Binary"/> 
-+      <any namespace="##other" processContents="lax" minOccurs="0"
-+       maxOccurs="unbounded"/>
-+    </sequence>
-+  </choice>
-+</complexType>
-+
-+<!-- End PGPData -->
-+
-+<!-- Begin SPKIData -->
-+
-+<element name="SPKIData" type="ds:SPKIDataType"/> 
-+<complexType name="SPKIDataType">
-+  <sequence maxOccurs="unbounded">
-+    <element name="SPKISexp" type="base64Binary"/>
-+    <any namespace="##other" processContents="lax" minOccurs="0"/>
-+  </sequence>
-+</complexType> 
-+
-+<!-- End SPKIData -->
-+
-+<!-- End KeyInfo -->
-+
-+<!-- Start Object (Manifest, SignatureProperty) -->
-+
-+<element name="Object" type="ds:ObjectType"/> 
-+<complexType name="ObjectType" mixed="true">
-+  <sequence minOccurs="0" maxOccurs="unbounded">
-+    <any namespace="##any" processContents="lax"/>
-+  </sequence>
-+  <attribute name="Id" type="ID" use="optional"/> 
-+  <attribute name="MimeType" type="string" use="optional"/> <!-- add a grep facet -->
-+  <attribute name="Encoding" type="anyURI" use="optional"/> 
-+</complexType>
-+
-+<element name="Manifest" type="ds:ManifestType"/> 
-+<complexType name="ManifestType">
-+  <sequence>
-+    <element ref="ds:Reference" maxOccurs="unbounded"/> 
-+  </sequence>
-+  <attribute name="Id" type="ID" use="optional"/> 
-+</complexType>
-+
-+<element name="SignatureProperties" type="ds:SignaturePropertiesType"/> 
-+<complexType name="SignaturePropertiesType">
-+  <sequence>
-+    <element ref="ds:SignatureProperty" maxOccurs="unbounded"/> 
-+  </sequence>
-+  <attribute name="Id" type="ID" use="optional"/> 
-+</complexType>
-+
-+   <element name="SignatureProperty" type="ds:SignaturePropertyType"/> 
-+   <complexType name="SignaturePropertyType" mixed="true">
-+     <choice maxOccurs="unbounded">
-+       <any namespace="##other" processContents="lax"/>
-+       <!-- (1,1) elements from (1,unbounded) namespaces -->
-+     </choice>
-+     <attribute name="Target" type="anyURI" use="required"/> 
-+     <attribute name="Id" type="ID" use="optional"/> 
-+   </complexType>
-+
-+<!-- End Object (Manifest, SignatureProperty) -->
-+
-+<!-- Start Algorithm Parameters -->
-+
-+<simpleType name="HMACOutputLengthType">
-+  <restriction base="integer"/>
-+</simpleType>
-+
-+<!-- Start KeyValue Element-types -->
-+
-+<element name="DSAKeyValue" type="ds:DSAKeyValueType"/>
-+<complexType name="DSAKeyValueType">
-+  <sequence>
-+    <sequence minOccurs="0">
-+      <element name="P" type="ds:CryptoBinary"/>
-+      <element name="Q" type="ds:CryptoBinary"/>
-+    </sequence>
-+    <element name="G" type="ds:CryptoBinary" minOccurs="0"/>
-+    <element name="Y" type="ds:CryptoBinary"/>
-+    <element name="J" type="ds:CryptoBinary" minOccurs="0"/>
-+    <sequence minOccurs="0">
-+      <element name="Seed" type="ds:CryptoBinary"/>
-+      <element name="PgenCounter" type="ds:CryptoBinary"/>
-+    </sequence>
-+  </sequence>
-+</complexType>
-+
-+<element name="RSAKeyValue" type="ds:RSAKeyValueType"/>
-+<complexType name="RSAKeyValueType">
-+  <sequence>
-+    <element name="Modulus" type="ds:CryptoBinary"/> 
-+    <element name="Exponent" type="ds:CryptoBinary"/> 
-+  </sequence>
-+</complexType> 
-+
-+<!-- End KeyValue Element-types -->
-+
-+<!-- End Signature -->
-+
-+</schema>
---- /dev/null	1970-01-01 00:00:00.000000000 +0000
-+++ python-zeep/tests/wsdl_files/soap_import_2.wsdl	2016-12-06 18:24:05.000000000 +0100
-@@ -0,0 +1,42 @@
-+<?xml version="1.0"?>
-+<definitions 
-+    xmlns:tns="http://example.com/stockquote.wsdl" 
-+    xmlns:xsd1="http://example.com/stockquote.xsd" 
-+    xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" 
-+    xmlns="http://schemas.xmlsoap.org/wsdl/"
-+    name="StockQuote" 
-+    targetNamespace="http://example.com/stockquote.wsdl">
-+  <types>
-+    <schema xmlns="http://www.w3.org/2001/XMLSchema" 
-+            targetNamespace="http://test.python-zeep.org/imports"
-+            xmlns:tns="http://test.python-zeep.org/imports">
-+      <import schemaLocation="test_import_2.xsd" namespace="http://example.com/stockquote.xsd"/>
-+    </schema>
-+    <schema xmlns="http://www.w3.org/2001/XMLSchema" 
-+            targetNamespace="http://test.python-zeep.org/something"
-+            xmlns:tns="http://test.python-zeep.org/something">
-+      <import namespace="http://schemas.xmlsoap.org/soap/encoding/" 
-+              schemaLocation="http://schemas.xmlsoap.org/soap/encoding/"/>
-+    </schema>
-+  </types>
-+  <message name="GetLastTradePriceInput">
-+    <part name="body" element="xsd1:TradePriceRequest"/>
-+  </message>
-+  <message name="GetLastTradePriceOutput">
-+    <part name="body" element="xsd1:TradePrice"/>
-+  </message>
-+  <message name="FaultMessageMsg1">
-+    <part name="fault1" element="xsd1:Fault1"/>
-+  </message>
-+  <message name="FaultMessageMsg2">
-+    <part name="fault2" element="xsd1:Fault2"/>
-+  </message>
-+  <portType name="StockQuotePortType">
-+    <operation name="GetLastTradePrice">
-+      <input message="tns:GetLastTradePriceInput"/>
-+      <output message="tns:GetLastTradePriceOutput"/>
-+      <fault message="tns:FaultMessageMsg1" name="fault1"/>
-+      <fault message="tns:FaultMessageMsg2" name="fault2"/>
-+    </operation>
-+  </portType>
-+</definitions>
---- /dev/null	1970-01-01 00:00:00.000000000 +0000
-+++ python-zeep/tests/wsdl_files/soap_import_main.wsdl	2016-12-06 18:24:05.000000000 +0100
-@@ -0,0 +1,38 @@
-+<?xml version="1.0"?>
-+<definitions 
-+    xmlns:tns="http://example.com/stockquote.wsdl" 
-+    xmlns:xsd1="http://example.com/stockquote.xsd" 
-+    xmlns:sub="http://test.python-zeep.org/sub"
-+    xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" 
-+    xmlns="http://schemas.xmlsoap.org/wsdl/"
-+    name="StockQuote" 
-+    targetNamespace="http://example.com/stockquote.wsdl">
-+  <import namespace="http://test.python-zeep.org/sub" location="soap_import_2.wsdl"/>
-+
-+  <types/>
-+  <binding name="StockQuoteBinding" type="tns:StockQuotePortType">
-+    <soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http"/>
-+    <operation name="GetLastTradePrice">
-+      <soap:operation soapAction="http://example.com/GetLastTradePrice"/>
-+      <input>
-+        <soap:body use="literal"/>
-+      </input>
-+      <output>
-+        <soap:body use="literal"/>
-+      </output>
-+      <fault name="fault1">
-+        <soap:fault name="fault1" use="literal"/>
-+      </fault>
-+      <fault name="fault2">
-+        <soap:fault name="fault2" use="literal"/>
-+      </fault>
-+    </operation>
-+  </binding>
-+  <service name="StockQuoteService">
-+    <documentation>My first service</documentation>
-+    <port name="StockQuotePort" binding="tns:StockQuoteBinding">
-+      <soap:address location="http://example.com/stockquote"/>
-+    </port>
-+  </service>
-+</definitions>
-+
diff --git a/debian/patches/series b/debian/patches/series
deleted file mode 100644
index 3b984e6..0000000
--- a/debian/patches/series
+++ /dev/null
@@ -1 +0,0 @@
-01-add-missing-xsd-files.patch
commit 5323e146dce708833f07fcff48e6bd4c5f19681d
Author: Mathias Behrle <mathiasb at m9s.biz>
Date:   Tue Jan 31 12:26:10 2017 +0100

    Merging upstream version 0.24.0.

diff --git a/.editorconfig b/.editorconfig
deleted file mode 100644
index 4926c2f..0000000
--- a/.editorconfig
+++ /dev/null
@@ -1,19 +0,0 @@
-root = true
-
-[*.py]
-line_length = 79
-multi_line_output = 4
-balanced_wrapping = true
-known_first_party = zeep,tests
-use_parentheses = true
-indent_style = space
-indent_size = 4
-tab_width = 4
-
-[*.yml]
-indent_size = 2
-shift_width = 2
-
-[Makefile]
-indent_style = tab
-indent_size = 4
diff --git a/.gitignore b/.gitignore
deleted file mode 100644
index 166db7a..0000000
--- a/.gitignore
+++ /dev/null
@@ -1,20 +0,0 @@
-*.egg-info
-*.pyc
-.tox
-.coverage
-.eggs
-.cache
-.python-version
-.venv
-.idea/
-/build/
-/dist/
-/test_clients/
-/docs/_build/
-/frutsels/
-/server/
-/htmlcov/
-
-
-# Editors
-.idea/
diff --git a/.travis.yml b/.travis.yml
deleted file mode 100644
index d2de223..0000000
--- a/.travis.yml
+++ /dev/null
@@ -1,40 +0,0 @@
----
-sudo: false
-language: python
-
-python:
-  - '2.7'
-  - '3.3'
-  - '3.4'
-  - '3.5'
-  - 'pypy'
-
-install:
-  - |
-      if [ "$TRAVIS_PYTHON_VERSION" = "pypy" ]; then
-        export PYENV_ROOT="$HOME/.pyenv"
-        if [ -f "$PYENV_ROOT/bin/pyenv" ]; then
-          pushd "$PYENV_ROOT" && git pull && popd
-        else
-          rm -rf "$PYENV_ROOT" && git clone --depth 1 https://github.com/yyuu/pyenv.git "$PYENV_ROOT"
-        fi
-        export PYPY_VERSION="5.4"
-        "$PYENV_ROOT/bin/pyenv" install --skip-existing "pypy-$PYPY_VERSION"
-        virtualenv --python="$PYENV_ROOT/versions/pypy-$PYPY_VERSION/bin/python" "$HOME/virtualenvs/pypy-$PYPY_VERSION"
-        source "$HOME/virtualenvs/pypy-$PYPY_VERSION/bin/activate"
-      fi
-  - pip install codecov
-  - pip install -e .[test]
-
-script:
-  - py.test --cov=zeep --cov-report=term-missing
-
-after_success:
-  - codecov
-
-before_cache:
-  - rm -rf $HOME/.cache/pip/log
-
-cache:
-  directories:
-    - $HOME/.cache/pip
diff --git a/CHANGES b/CHANGES
index 468f461..1b6c737 100644
--- a/CHANGES
+++ b/CHANGES
@@ -1,3 +1,20 @@
+0.24.0 (2016-12-16)
+-------------------
+ - Don't fail the parsing of responses if an xsi:type references an non-existing
+   type. Instead log a message (#273)
+ - Fix serializing etree.Element instances in the helpers.serialize function
+   (#255)
+ - Fix infinite loop during parsing of xsd.Sequence where max_occurs is 
+   unbounded (#256)
+ - Make the xsd.Element name kwarg required
+ - Improve handling of the xsd:anyType element when passed instances of 
+   complexType's (#252)
+ - Silently ignore unsupported binding transports instead of an hard error 
+   (#277)
+ - Support microseconds for xsd.dateTime and xsd.Time (#280)
+ - Don't mutate passed values to the zeep operations (#280)
+
+
 0.23.0 (2016-11-24)
 -------------------
  - Add Client.set_default_soapheaders() to set soapheaders which are to be used
diff --git a/Makefile b/Makefile
deleted file mode 100644
index 3244331..0000000
--- a/Makefile
+++ /dev/null
@@ -1,30 +0,0 @@
-.PHONY: install clean test retest coverage docs
-
-install:
-	pip install -e .[docs,test]
-	pip install bumpversion twine wheel
-
-lint:
-	flake8 src/ tests/
-	isort --recursive --check-only --diff src tests
-
-clean:
-	find . -name '*.pyc' -delete
-
-test:
-	py.test -vvv
-
-retest:
-	py.test -vvv --lf
-
-coverage:
-	py.test --cov=zeep --cov-report=term-missing --cov-report=html
-
-docs:
-	$(MAKE) -C docs html
-
-release:
-	pip install twine wheel
-	rm -rf dist/*
-	python setup.py sdist bdist_wheel
-	twine upload -s dist/*
diff --git a/PKG-INFO b/PKG-INFO
index 0810ebd..fc65fe8 100644
--- a/PKG-INFO
+++ b/PKG-INFO
@@ -1,6 +1,6 @@
 Metadata-Version: 1.1
 Name: zeep
-Version: 0.23.0
+Version: 0.24.0
 Summary: A modern/fast Python SOAP client based on lxml / requests
 Home-page: http://docs.python-zeep.org
 Author: Michael van Tellingen
@@ -69,8 +69,10 @@ Description: ========================
         If you encounter bugs then please `let me know`_ .  A copy of the WSDL file if
         possible would be most helpful. 
         
-        I'm also able to offer commercial support.  Please contact me at
-        info at mvantellingen.nl for more information.
+        I'm also able to offer commercial support.  As in contracting work. Please
+        contact me at info at mvantellingen.nl for more information. If you just have a
+        random question and don't intent to actually pay me for my support then please
+        DO NOT email me at that e-mail address but just use stackoverflow or something..
         
         .. _let me know: https://github.com/mvantellingen/python-zeep/issues
         
diff --git a/README.rst b/README.rst
index 81d782f..e46a8b6 100644
--- a/README.rst
+++ b/README.rst
@@ -84,7 +84,9 @@ Support
 If you encounter bugs then please `let me know`_ .  A copy of the WSDL file if
 possible would be most helpful. 
 
-I'm also able to offer commercial support.  Please contact me at
-info at mvantellingen.nl for more information.
+I'm also able to offer commercial support.  As in contracting work. Please
+contact me at info at mvantellingen.nl for more information. If you just have a
+random question and don't intent to actually pay me for my support then please
+DO NOT email me at that e-mail address but just use stackoverflow or something..
 
 .. _let me know: https://github.com/mvantellingen/python-zeep/issues
diff --git a/docs/Makefile b/docs/Makefile
deleted file mode 100644
index 49c4227..0000000
--- a/docs/Makefile
+++ /dev/null
@@ -1,216 +0,0 @@
-# Makefile for Sphinx documentation
-#
-
-# You can set these variables from the command line.
-SPHINXOPTS    =
-SPHINXBUILD   = sphinx-build
-PAPER         =
-BUILDDIR      = _build
-
-# User-friendly check for sphinx-build
-ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1)
-$(error The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don't have Sphinx installed, grab it from http://sphinx-doc.org/)
-endif
-
-# Internal variables.
-PAPEROPT_a4     = -D latex_paper_size=a4
-PAPEROPT_letter = -D latex_paper_size=letter
-ALLSPHINXOPTS   = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
-# the i18n builder cannot share the environment and doctrees with the others
-I18NSPHINXOPTS  = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
-
-.PHONY: help
-help:
-	@echo "Please use \`make <target>' where <target> is one of"
-	@echo "  html       to make standalone HTML files"
-	@echo "  dirhtml    to make HTML files named index.html in directories"
-	@echo "  singlehtml to make a single large HTML file"
-	@echo "  pickle     to make pickle files"
-	@echo "  json       to make JSON files"
-	@echo "  htmlhelp   to make HTML files and a HTML help project"
-	@echo "  qthelp     to make HTML files and a qthelp project"
-	@echo "  applehelp  to make an Apple Help Book"
-	@echo "  devhelp    to make HTML files and a Devhelp project"
-	@echo "  epub       to make an epub"
-	@echo "  latex      to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
-	@echo "  latexpdf   to make LaTeX files and run them through pdflatex"
-	@echo "  latexpdfja to make LaTeX files and run them through platex/dvipdfmx"
-	@echo "  text       to make text files"
-	@echo "  man        to make manual pages"
-	@echo "  texinfo    to make Texinfo files"
-	@echo "  info       to make Texinfo files and run them through makeinfo"
-	@echo "  gettext    to make PO message catalogs"
-	@echo "  changes    to make an overview of all changed/added/deprecated items"
-	@echo "  xml        to make Docutils-native XML files"
-	@echo "  pseudoxml  to make pseudoxml-XML files for display purposes"
-	@echo "  linkcheck  to check all external links for integrity"
-	@echo "  doctest    to run all doctests embedded in the documentation (if enabled)"
-	@echo "  coverage   to run coverage check of the documentation (if enabled)"
-
-.PHONY: clean
-clean:
-	rm -rf $(BUILDDIR)/*
-
-.PHONY: html
-html:
-	$(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
-	@echo
-	@echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
-
-.PHONY: dirhtml
-dirhtml:
-	$(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml
-	@echo
-	@echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml."
-
-.PHONY: singlehtml
-singlehtml:
-	$(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml
-	@echo
-	@echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml."
-
-.PHONY: pickle
-pickle:
-	$(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle
-	@echo
-	@echo "Build finished; now you can process the pickle files."
-
-.PHONY: json
-json:
-	$(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json
-	@echo
-	@echo "Build finished; now you can process the JSON files."
-
-.PHONY: htmlhelp
-htmlhelp:
-	$(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp
-	@echo
-	@echo "Build finished; now you can run HTML Help Workshop with the" \
-	      ".hhp project file in $(BUILDDIR)/htmlhelp."
-
-.PHONY: qthelp
-qthelp:
-	$(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp
-	@echo
-	@echo "Build finished; now you can run "qcollectiongenerator" with the" \
-	      ".qhcp project file in $(BUILDDIR)/qthelp, like this:"
-	@echo "# qcollectiongenerator $(BUILDDIR)/qthelp/Zeep.qhcp"
-	@echo "To view the help file:"
-	@echo "# assistant -collectionFile $(BUILDDIR)/qthelp/Zeep.qhc"
-
-.PHONY: applehelp
-applehelp:
-	$(SPHINXBUILD) -b applehelp $(ALLSPHINXOPTS) $(BUILDDIR)/applehelp
-	@echo
-	@echo "Build finished. The help book is in $(BUILDDIR)/applehelp."
-	@echo "N.B. You won't be able to view it unless you put it in" \
-	      "~/Library/Documentation/Help or install it in your application" \
-	      "bundle."
-
-.PHONY: devhelp
-devhelp:
-	$(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp
-	@echo
-	@echo "Build finished."
-	@echo "To view the help file:"
-	@echo "# mkdir -p $$HOME/.local/share/devhelp/Zeep"
-	@echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/Zeep"
-	@echo "# devhelp"
-
-.PHONY: epub
-epub:
-	$(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub
-	@echo
-	@echo "Build finished. The epub file is in $(BUILDDIR)/epub."
-
-.PHONY: latex
-latex:
-	$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
-	@echo
-	@echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex."
-	@echo "Run \`make' in that directory to run these through (pdf)latex" \
-	      "(use \`make latexpdf' here to do that automatically)."
-
-.PHONY: latexpdf
-latexpdf:
-	$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
-	@echo "Running LaTeX files through pdflatex..."
-	$(MAKE) -C $(BUILDDIR)/latex all-pdf
-	@echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
-
-.PHONY: latexpdfja
-latexpdfja:
-	$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
-	@echo "Running LaTeX files through platex and dvipdfmx..."
-	$(MAKE) -C $(BUILDDIR)/latex all-pdf-ja
-	@echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
-
-.PHONY: text
-text:
-	$(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text
-	@echo
-	@echo "Build finished. The text files are in $(BUILDDIR)/text."
-
-.PHONY: man
-man:
-	$(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man
-	@echo
-	@echo "Build finished. The manual pages are in $(BUILDDIR)/man."
-
-.PHONY: texinfo
-texinfo:
-	$(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
-	@echo
-	@echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo."
-	@echo "Run \`make' in that directory to run these through makeinfo" \
-	      "(use \`make info' here to do that automatically)."
-
-.PHONY: info
-info:
-	$(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
-	@echo "Running Texinfo files through makeinfo..."
-	make -C $(BUILDDIR)/texinfo info
-	@echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo."
-
-.PHONY: gettext
-gettext:
-	$(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale
-	@echo
-	@echo "Build finished. The message catalogs are in $(BUILDDIR)/locale."
-
-.PHONY: changes
-changes:
-	$(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes
-	@echo
-	@echo "The overview file is in $(BUILDDIR)/changes."
-
-.PHONY: linkcheck
-linkcheck:
-	$(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck
-	@echo
-	@echo "Link check complete; look for any errors in the above output " \
-	      "or in $(BUILDDIR)/linkcheck/output.txt."
-
-.PHONY: doctest
-doctest:
-	$(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest
-	@echo "Testing of doctests in the sources finished, look at the " \
-	      "results in $(BUILDDIR)/doctest/output.txt."
-
-.PHONY: coverage
-coverage:
-	$(SPHINXBUILD) -b coverage $(ALLSPHINXOPTS) $(BUILDDIR)/coverage
-	@echo "Testing of coverage in the sources finished, look at the " \
-	      "results in $(BUILDDIR)/coverage/python.txt."
-
-.PHONY: xml
-xml:
-	$(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml
-	@echo
-	@echo "Build finished. The XML files are in $(BUILDDIR)/xml."
-
-.PHONY: pseudoxml
-pseudoxml:
-	$(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml
-	@echo
-	@echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml."
diff --git a/docs/_templates/sidebar-intro.html b/docs/_templates/sidebar-intro.html
deleted file mode 100644
index af3fb9e..0000000
--- a/docs/_templates/sidebar-intro.html
+++ /dev/null
@@ -1,18 +0,0 @@
-
-<a href="{{ pathto(master_doc) }}">
-    <img class="logo" src="{{ pathto('_static/zeep-logo.png', 1) }}">
-</a>
-
-<p>Zeep is a modern SOAP client for Python</p>
-<p>
-  <iframe src="http://ghbtns.com/github-btn.html?user=mvantellingen&repo=python-zeep&type=watch&count=true&size=large"
-    allowtransparency="true" frameborder="0" scrolling="0" width="200px" height="35px"></iframe>
-</p>
-
-<h3>Links</h3>
-<ul>
-  <li><a href="http://github.com/mvantellingen/python-zeep">Zeep @ GitHub</a></li>
-  <li><a href="http://pypi.python.org/pypi/zeep">Zeep @ PyPI</a></li>
-  <li><a href="http://github.com/mvantellingen/python-zeep/issues">Issue Tracker</a></li>
-</ul>
-
diff --git a/docs/conf.py b/docs/conf.py
deleted file mode 100644
index b233225..0000000
--- a/docs/conf.py
+++ /dev/null
@@ -1,299 +0,0 @@
-# -*- coding: utf-8 -*-
-#
-# Zeep documentation build configuration file, created by
-# sphinx-quickstart on Fri Mar  4 16:51:06 2016.
-#
-# This file is execfile()d with the current directory set to its
-# containing dir.
-#
-# Note that not all possible configuration values are present in this
-# autogenerated file.
-#
-# All configuration values have a default; values that are commented out
-# serve to show the default.
-
-import sys
-import os
-import pkg_resources
-
-# If extensions (or modules to document with autodoc) are in another directory,
-# add these directories to sys.path here. If the directory is relative to the
-# documentation root, use os.path.abspath to make it absolute, like shown here.
-#sys.path.insert(0, os.path.abspath('.'))
-
-# -- General configuration ------------------------------------------------
-
-# If your documentation needs a minimal Sphinx version, state it here.
-#needs_sphinx = '1.0'
-
-# Add any Sphinx extension module names here, as strings. They can be
-# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
-# ones.
-extensions = ['sphinx.ext.autodoc']
-
-# Add any paths that contain templates here, relative to this directory.
-templates_path = ['_templates']
-
-# The suffix(es) of source filenames.
-# You can specify multiple suffix as a list of string:
-# source_suffix = ['.rst', '.md']
-source_suffix = '.rst'
-
-# The encoding of source files.
-#source_encoding = 'utf-8-sig'
-
-# The master toctree document.
-master_doc = 'index'
-
-# General information about the project.
-project = u'Zeep'
-copyright = u'2016, <a href="https://www.mvantellingen.nl/">Michael van Tellingen</a>'
-author = u'Michael van Tellingen'
-
-
-# The version info for the project you're documenting, acts as replacement for
-# |version| and |release|, also used in various other places throughout the
-# built documents.
-#
-# The short X.Y version.
-version = '0.23.0'
-release = version
-
-# The language for content autogenerated by Sphinx. Refer to documentation
-# for a list of supported languages.
-#
-# This is also used if you do content translation via gettext catalogs.
-# Usually you set "language" from the command line for these cases.
-language = None
-
-# There are two options for replacing |today|: either, you set today to some
-# non-false value, then it is used:
-#today = ''
-# Else, today_fmt is used as the format for a strftime call.
-#today_fmt = '%B %d, %Y'
-
-# List of patterns, relative to source directory, that match files and
-# directories to ignore when looking for source files.
-exclude_patterns = ['_build']
-
-# The reST default role (used for this markup: `text`) to use for all
-# documents.
-#default_role = None
-
-# If true, '()' will be appended to :func: etc. cross-reference text.
-#add_function_parentheses = True
-
-# If true, the current module name will be prepended to all description
-# unit titles (such as .. function::).
-#add_module_names = True
-
-# If true, sectionauthor and moduleauthor directives will be shown in the
-# output. They are ignored by default.
-#show_authors = False
-
-# The name of the Pygments (syntax highlighting) style to use.
-pygments_style = 'sphinx'
-
-# A list of ignored prefixes for module index sorting.
-#modindex_common_prefix = []
-
-# If true, keep warnings as "system message" paragraphs in the built documents.
-#keep_warnings = False
-
-# If true, `todo` and `todoList` produce output, else they produce nothing.
-todo_include_todos = False
-
-
-autodoc_default_flags = [':members:']
-
-
-# -- Options for HTML output ----------------------------------------------
-
-# The theme to use for HTML and HTML Help pages.  See the documentation for
-# a list of builtin themes.
-html_theme = 'alabaster'
-
-# Theme options are theme-specific and customize the look and feel of a theme
-# further.  For a list of options available for each theme, see the
-# documentation.
-html_theme_options = {
-    'github_user': 'mvantellingen',
-    'github_banner': True,
-    'github_repo': 'python-zeep',
-    'travis_button': True,
-    'codecov_button': True,
-    'analytics_id':  'UA-75907833-1',
-}
-
-# Add any paths that contain custom themes here, relative to this directory.
-#html_theme_path = []
-
-# The name for this set of Sphinx documents.  If None, it defaults to
-# "<project> v<release> documentation".
-#html_title = None
-
-# A shorter title for the navigation bar.  Default is the same as html_title.
-#html_short_title = None
-
-# The name of an image file (relative to this directory) to place at the top
-# of the sidebar.
-#html_logo = None
-
-# The name of an image file (relative to this directory) to use as a favicon of
-# the docs.  This file should be a Windows icon file (.ico) being 16x16 or 32x32
-# pixels large.
-#html_favicon = None
-
-# Add any paths that contain custom static files (such as style sheets) here,
-# relative to this directory. They are copied after the builtin static files,
-# so a file named "default.css" will overwrite the builtin "default.css".
-html_static_path = ['_static']
-
-# Add any extra paths that contain custom files (such as robots.txt or
-# .htaccess) here, relative to this directory. These files are copied
-# directly to the root of the documentation.
-#html_extra_path = []
-
-# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
-# using the given strftime format.
-#html_last_updated_fmt = '%b %d, %Y'
-
-# If true, SmartyPants will be used to convert quotes and dashes to
-# typographically correct entities.
-#html_use_smartypants = True
-
-# Custom sidebar templates, maps document names to template names.
-html_sidebars = {
-    '*': [
-        'sidebar-intro.html', 'globaltoc.html', 'sourcelink.html',
-        'searchbox.html'
-    ]
-}
-
-# Additional templates that should be rendered to pages, maps page names to
-# template names.
-#html_additional_pages = {}
-
-# If false, no module index is generated.
-#html_domain_indices = True
-
-# If false, no index is generated.
-#html_use_index = True
-
-# If true, the index is split into individual pages for each letter.
-#html_split_index = False
-
-# If true, links to the reST sources are added to the pages.
-#html_show_sourcelink = True
-
-# If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
-#html_show_sphinx = True
-
-# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
-#html_show_copyright = True
-
-# If true, an OpenSearch description file will be output, and all pages will
-# contain a <link> tag referring to it.  The value of this option must be the
-# base URL from which the finished HTML is served.
-#html_use_opensearch = ''
-
-# This is the file name suffix for HTML files (e.g. ".xhtml").
-#html_file_suffix = None
-
-# Language to be used for generating the HTML full-text search index.
-# Sphinx supports the following languages:
-#   'da', 'de', 'en', 'es', 'fi', 'fr', 'hu', 'it', 'ja'
-#   'nl', 'no', 'pt', 'ro', 'ru', 'sv', 'tr'
-#html_search_language = 'en'
-
-# A dictionary with options for the search language support, empty by default.
-# Now only 'ja' uses this config value
-#html_search_options = {'type': 'default'}
-
-# The name of a javascript file (relative to the configuration directory) that
-# implements a search results scorer. If empty, the default will be used.
-#html_search_scorer = 'scorer.js'
-
-# Output file base name for HTML help builder.
-htmlhelp_basename = 'Zeepdoc'
-
-# -- Options for LaTeX output ---------------------------------------------
-
-latex_elements = {
-# The paper size ('letterpaper' or 'a4paper').
-#'papersize': 'letterpaper',
-
-# The font size ('10pt', '11pt' or '12pt').
-#'pointsize': '10pt',
-
-# Additional stuff for the LaTeX preamble.
-#'preamble': '',
-
-# Latex figure (float) alignment
-#'figure_align': 'htbp',
-}
-
-# Grouping the document tree into LaTeX files. List of tuples
-# (source start file, target name, title,
-#  author, documentclass [howto, manual, or own class]).
-latex_documents = [
-    (master_doc, 'Zeep.tex', u'Zeep Documentation',
-     u'Michael van Tellingen', 'manual'),
-]
-
-# The name of an image file (relative to this directory) to place at the top of
-# the title page.
-#latex_logo = None
-
-# For "manual" documents, if this is true, then toplevel headings are parts,
-# not chapters.
-#latex_use_parts = False
-
-# If true, show page references after internal links.
-#latex_show_pagerefs = False
-
-# If true, show URL addresses after external links.
-#latex_show_urls = False
-
-# Documents to append as an appendix to all manuals.
-#latex_appendices = []
-
-# If false, no module index is generated.
-#latex_domain_indices = True
-
-
-# -- Options for manual page output ---------------------------------------
-
-# One entry per manual page. List of tuples
-# (source start file, name, description, authors, manual section).
-man_pages = [
-    (master_doc, 'zeep', u'Zeep Documentation',
-     [author], 1)
-]
-
-# If true, show URL addresses after external links.
-#man_show_urls = False
-
-
-# -- Options for Texinfo output -------------------------------------------
-
-# Grouping the document tree into Texinfo files. List of tuples
-# (source start file, target name, title, author,
-#  dir menu entry, description, category)
-texinfo_documents = [
-    (master_doc, 'Zeep', u'Zeep Documentation',
-     author, 'Zeep', 'One line description of project.',
-     'Miscellaneous'),
-]
-
-# Documents to append as an appendix to all manuals.
-#texinfo_appendices = []
-
-# If false, no module index is generated.
-#texinfo_domain_indices = True
-
-# How to display URL addresses: 'footnote', 'no', or 'inline'.
-#texinfo_show_urls = 'footnote'
-
-# If true, do not generate a @detailmenu in the "Top" node's menu.
-#texinfo_no_detailmenu = False
diff --git a/docs/index.rst b/docs/index.rst
deleted file mode 100644
index 2aeb016..0000000
--- a/docs/index.rst
+++ /dev/null
@@ -1,131 +0,0 @@
-========================
-Zeep: Python SOAP client 
-========================
-
-A fast and modern Python SOAP client
-
-Highlights:
- * Modern codebase compatible with Python 2.7, 3.3, 3.4, 3.5 and PyPy
- * Build on top of lxml and requests
- * Supports recursive WSDL and XSD documents.
- * Supports the xsd:choice and xsd:any elements.
- * Uses the defusedxml module for handling potential XML security issues
- * Support for WSSE (UsernameToken only for now)
- * Experimental support for HTTP bindings
- * Experimental support for WS-Addressing headers
- * Experimental support for asyncio via aiohttp (Python 3.5+)
-
-Features still in development include:
- * WSSE x.509 support (BinarySecurityToken)
- * WS Policy support
-
-
-A simple example:
-
-.. code-block:: python
-
-    from zeep import Client
-
-    client = Client('http://www.webservicex.net/ConvertSpeed.asmx?WSDL')
-    result = client.service.ConvertSpeed(
-        100, 'kilometersPerhour', 'milesPerhour')
-
-    assert result == 62.137
-
-
-Quick Introduction
-==================
-
-Zeep inspects the wsdl document and generates the corresponding bindings.  This
-provides an easy to use programmatic interface to a soap server.
-
-The emphasis is on Soap 1.1 and Soap 1.2, however Zeep also offers experimental
-support for HTTP Get and Post bindings.
-
-Parsing the XML documents is done by using the lxml library. This is the most
-performant and compliant Python XML library currently available. This results
-in major speed benefits when retrieving large soap responses.
-
-The SOAP specifications are unfortunately really vague and leave a lot of
-things open for interpretation.  Due to this there are a lot of WSDL documents
-available which are invalid or SOAP servers which contain bugs. Zeep tries to
-be as compatible as possible but there might be cases where you run into 
-problems. Don't hesitate to submit an issue in this case (please see 
-:ref:`reporting_bugs`).
-
-
-Getting started
-===============
-
-You can install the latest version of zeep using pip::
-
-    pip install zeep
-
-The first thing you generally want to do is inspect the wsdl file you need to
-implement. This can be done with::
-
-    python -mzeep <wsdl>
-
-
-See ``python -mzeep --help`` for more information about this command.
-
-
-.. note:: Since this module hasn't reached 1.0.0 yet their might be minor
-          releases which introduce backwards compatible changes. While I try 
-          to keep this to a minimum it can still happen. So as always pin the 
-          version of zeep you used (e.g. ``zeep==0.14.0``').
-
-
-
-A simple use-case
------------------
-
-To give you an idea how zeep works a basic example.
-
-.. code-block:: python
-
-    import zeep
-
-    wsdl = 'http://www.soapclient.com/xml/soapresponder.wsdl'
-    client = zeep.Client(wsdl=wsdl)
-    print(client.service.Method1('Zeep', 'is cool'))
-
-The WSDL used above only defines one simple function (``Method1``) which is 
-made available by zeep via ``client.service.Method1``. It takes two arguments
-and returns a string. To get an overview of the services available on the 
-endpoint you can run the following command in your terminal.
-
-.. code-block:: bash
-
-    python -mzeep http://www.soapclient.com/xml/soapresponder.wsdl
-
-
-More information
-================
-
-.. toctree::
-   :maxdepth: 2
-   :name: mastertoc
-
-   in_depth
-   datastructures
-   transport
-   wsa
-   wsse
-   plugins
-   helpers
-   reporting_bugs
-   changes
-
-
-Support
-=======
-
-If you encounter bugs then please `let me know`_ . Please see :doc:`reporting_bugs`
-for information how to best report them.
-
-I'm also able to offer commercial support.  Please contact me at
-info at mvantellingen.nl for more information.
-
-
-.. _let me know: https://github.com/mvantellingen/python-zeep/issues
diff --git a/docs/make.bat b/docs/make.bat
deleted file mode 100644
index bb121e7..0000000
--- a/docs/make.bat
+++ /dev/null
@@ -1,263 +0,0 @@
- at ECHO OFF
-
-REM Command file for Sphinx documentation
-
-if "%SPHINXBUILD%" == "" (
-	set SPHINXBUILD=sphinx-build
-)
-set BUILDDIR=_build
-set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% .
-set I18NSPHINXOPTS=%SPHINXOPTS% .
-if NOT "%PAPER%" == "" (
-	set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS%
-	set I18NSPHINXOPTS=-D latex_paper_size=%PAPER% %I18NSPHINXOPTS%
-)
-
-if "%1" == "" goto help
-
-if "%1" == "help" (
-	:help
-	echo.Please use `make ^<target^>` where ^<target^> is one of
-	echo.  html       to make standalone HTML files
-	echo.  dirhtml    to make HTML files named index.html in directories
-	echo.  singlehtml to make a single large HTML file
-	echo.  pickle     to make pickle files
-	echo.  json       to make JSON files
-	echo.  htmlhelp   to make HTML files and a HTML help project
-	echo.  qthelp     to make HTML files and a qthelp project
-	echo.  devhelp    to make HTML files and a Devhelp project
-	echo.  epub       to make an epub
-	echo.  latex      to make LaTeX files, you can set PAPER=a4 or PAPER=letter
-	echo.  text       to make text files
-	echo.  man        to make manual pages
-	echo.  texinfo    to make Texinfo files
-	echo.  gettext    to make PO message catalogs
-	echo.  changes    to make an overview over all changed/added/deprecated items
-	echo.  xml        to make Docutils-native XML files
-	echo.  pseudoxml  to make pseudoxml-XML files for display purposes
-	echo.  linkcheck  to check all external links for integrity
-	echo.  doctest    to run all doctests embedded in the documentation if enabled
-	echo.  coverage   to run coverage check of the documentation if enabled
-	goto end
-)
-
-if "%1" == "clean" (
-	for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i
-	del /q /s %BUILDDIR%\*
-	goto end
-)
-
-
-REM Check if sphinx-build is available and fallback to Python version if any
-%SPHINXBUILD% 1>NUL 2>NUL
-if errorlevel 9009 goto sphinx_python
-goto sphinx_ok
-
-:sphinx_python
-
-set SPHINXBUILD=python -m sphinx.__init__
-%SPHINXBUILD% 2> nul
-if errorlevel 9009 (
-	echo.
-	echo.The 'sphinx-build' command was not found. Make sure you have Sphinx
-	echo.installed, then set the SPHINXBUILD environment variable to point
-	echo.to the full path of the 'sphinx-build' executable. Alternatively you
-	echo.may add the Sphinx directory to PATH.
-	echo.
-	echo.If you don't have Sphinx installed, grab it from
-	echo.http://sphinx-doc.org/
-	exit /b 1
-)
-
-:sphinx_ok
-
-
-if "%1" == "html" (
-	%SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html
-	if errorlevel 1 exit /b 1
-	echo.
-	echo.Build finished. The HTML pages are in %BUILDDIR%/html.
-	goto end
-)
-
-if "%1" == "dirhtml" (
-	%SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml
-	if errorlevel 1 exit /b 1
-	echo.
-	echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml.
-	goto end
-)
-
-if "%1" == "singlehtml" (
-	%SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml
-	if errorlevel 1 exit /b 1
-	echo.
-	echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml.
-	goto end
-)
-
-if "%1" == "pickle" (
-	%SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle
-	if errorlevel 1 exit /b 1
-	echo.
-	echo.Build finished; now you can process the pickle files.
-	goto end
-)
-
-if "%1" == "json" (
-	%SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json
-	if errorlevel 1 exit /b 1
-	echo.
-	echo.Build finished; now you can process the JSON files.
-	goto end
-)
-
-if "%1" == "htmlhelp" (
-	%SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp
-	if errorlevel 1 exit /b 1
-	echo.
-	echo.Build finished; now you can run HTML Help Workshop with the ^
-.hhp project file in %BUILDDIR%/htmlhelp.
-	goto end
-)
-
-if "%1" == "qthelp" (
-	%SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp
-	if errorlevel 1 exit /b 1
-	echo.
-	echo.Build finished; now you can run "qcollectiongenerator" with the ^
-.qhcp project file in %BUILDDIR%/qthelp, like this:
-	echo.^> qcollectiongenerator %BUILDDIR%\qthelp\Zeep.qhcp
-	echo.To view the help file:
-	echo.^> assistant -collectionFile %BUILDDIR%\qthelp\Zeep.ghc
-	goto end
-)
-
-if "%1" == "devhelp" (
-	%SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp
-	if errorlevel 1 exit /b 1
-	echo.
-	echo.Build finished.
-	goto end
-)
-
-if "%1" == "epub" (
-	%SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub
-	if errorlevel 1 exit /b 1
-	echo.
-	echo.Build finished. The epub file is in %BUILDDIR%/epub.
-	goto end
-)
-
-if "%1" == "latex" (
-	%SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex
-	if errorlevel 1 exit /b 1
-	echo.
-	echo.Build finished; the LaTeX files are in %BUILDDIR%/latex.
-	goto end
-)
-
-if "%1" == "latexpdf" (
-	%SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex
-	cd %BUILDDIR%/latex
-	make all-pdf
-	cd %~dp0
-	echo.
-	echo.Build finished; the PDF files are in %BUILDDIR%/latex.
-	goto end
-)
-
-if "%1" == "latexpdfja" (
-	%SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex
-	cd %BUILDDIR%/latex
-	make all-pdf-ja
-	cd %~dp0
-	echo.
-	echo.Build finished; the PDF files are in %BUILDDIR%/latex.
-	goto end
-)
-
-if "%1" == "text" (
-	%SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text
-	if errorlevel 1 exit /b 1
-	echo.
-	echo.Build finished. The text files are in %BUILDDIR%/text.
-	goto end
-)
-
-if "%1" == "man" (
-	%SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man
-	if errorlevel 1 exit /b 1
-	echo.
-	echo.Build finished. The manual pages are in %BUILDDIR%/man.
-	goto end
-)
-
-if "%1" == "texinfo" (
-	%SPHINXBUILD% -b texinfo %ALLSPHINXOPTS% %BUILDDIR%/texinfo
-	if errorlevel 1 exit /b 1
-	echo.
-	echo.Build finished. The Texinfo files are in %BUILDDIR%/texinfo.
-	goto end
-)
-
-if "%1" == "gettext" (
-	%SPHINXBUILD% -b gettext %I18NSPHINXOPTS% %BUILDDIR%/locale
-	if errorlevel 1 exit /b 1
-	echo.
-	echo.Build finished. The message catalogs are in %BUILDDIR%/locale.
-	goto end
-)
-
-if "%1" == "changes" (
-	%SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes
-	if errorlevel 1 exit /b 1
-	echo.
-	echo.The overview file is in %BUILDDIR%/changes.
-	goto end
-)
-
-if "%1" == "linkcheck" (
-	%SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck
-	if errorlevel 1 exit /b 1
-	echo.
-	echo.Link check complete; look for any errors in the above output ^
-or in %BUILDDIR%/linkcheck/output.txt.
-	goto end
-)
-
-if "%1" == "doctest" (
-	%SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest
-	if errorlevel 1 exit /b 1
-	echo.
-	echo.Testing of doctests in the sources finished, look at the ^
-results in %BUILDDIR%/doctest/output.txt.
-	goto end
-)
-
-if "%1" == "coverage" (
-	%SPHINXBUILD% -b coverage %ALLSPHINXOPTS% %BUILDDIR%/coverage
-	if errorlevel 1 exit /b 1
-	echo.
-	echo.Testing of coverage in the sources finished, look at the ^
-results in %BUILDDIR%/coverage/python.txt.
-	goto end
-)
-
-if "%1" == "xml" (
-	%SPHINXBUILD% -b xml %ALLSPHINXOPTS% %BUILDDIR%/xml
-	if errorlevel 1 exit /b 1
-	echo.
-	echo.Build finished. The XML files are in %BUILDDIR%/xml.
-	goto end
-)
-
-if "%1" == "pseudoxml" (
-	%SPHINXBUILD% -b pseudoxml %ALLSPHINXOPTS% %BUILDDIR%/pseudoxml
-	if errorlevel 1 exit /b 1
-	echo.
-	echo.Build finished. The pseudo-XML files are in %BUILDDIR%/pseudoxml.
-	goto end
-)
-
-:end
diff --git a/examples/code39.py b/examples/code39.py
deleted file mode 100644
index eda664f..0000000
--- a/examples/code39.py
+++ /dev/null
@@ -1,8 +0,0 @@
-from __future__ import print_function
-import zeep
-
-
-client = zeep.Client(
-    wsdl='http://www.webservicex.net/barcode.asmx?WSDL')
-response = client.service.Code39('1234', 20, ShowCodeString=True, Title='ZEEP')
-print(repr(response))
diff --git a/examples/echo_services.py b/examples/echo_services.py
deleted file mode 100644
index 5270f95..0000000
--- a/examples/echo_services.py
+++ /dev/null
@@ -1,5 +0,0 @@
-from zeep.client import Client
-
-# RPC style soap service
-client = Client('http://www.soapclient.com/xml/soapresponder.wsdl')
-print(client.service.Method1('zeep', 'soap'))
diff --git a/examples/eu_vat_service.py b/examples/eu_vat_service.py
deleted file mode 100644
index e5ba15e..0000000
--- a/examples/eu_vat_service.py
+++ /dev/null
@@ -1,7 +0,0 @@
-from __future__ import print_function
-import zeep
-
-
-client = zeep.Client(
-    wsdl='http://ec.europa.eu/taxation_customs/vies/checkVatService.wsdl')
-print(client.service.checkVat('NL', '170944128B01'))
diff --git a/examples/km_to_miles.py b/examples/km_to_miles.py
deleted file mode 100644
index d8821df..0000000
--- a/examples/km_to_miles.py
+++ /dev/null
@@ -1,16 +0,0 @@
-from __future__ import print_function
-import zeep
-
-
-client = zeep.Client(
-    wsdl='http://www.webservicex.net/ConvertSpeed.asmx?WSDL')
-
-client.wsdl.dump()
-
-print (client.service.ConvertSpeed(100, 'kilometersPerhour', 'milesPerhour'))
-
-http_get = client.bind('ConvertSpeeds', 'ConvertSpeedsHttpGet')
-http_post = client.bind('ConvertSpeeds', 'ConvertSpeedsHttpPost')
-
-print(http_get.ConvertSpeed(100, 'kilometersPerhour', 'milesPerhour'))
-print(http_post.ConvertSpeed(100, 'kilometersPerhour', 'milesPerhour'))
diff --git a/setup.py b/setup.py
index 8ba07bf..ea27113 100755
--- a/setup.py
+++ b/setup.py
@@ -37,7 +37,7 @@ with open('README.rst') as fh:
 
 setup(
     name='zeep',
-    version='0.23.0',
+    version='0.24.0',
     description='A modern/fast Python SOAP client based on lxml / requests',
     long_description=long_description,
     author="Michael van Tellingen",
diff --git a/PKG-INFO b/src/zeep.egg-info/PKG-INFO
similarity index 88%
copy from PKG-INFO
copy to src/zeep.egg-info/PKG-INFO
index 0810ebd..fc65fe8 100644
--- a/PKG-INFO
+++ b/src/zeep.egg-info/PKG-INFO
@@ -1,6 +1,6 @@
 Metadata-Version: 1.1
 Name: zeep
-Version: 0.23.0
+Version: 0.24.0
 Summary: A modern/fast Python SOAP client based on lxml / requests
 Home-page: http://docs.python-zeep.org
 Author: Michael van Tellingen
@@ -69,8 +69,10 @@ Description: ========================
         If you encounter bugs then please `let me know`_ .  A copy of the WSDL file if
         possible would be most helpful. 
         
-        I'm also able to offer commercial support.  Please contact me at
-        info at mvantellingen.nl for more information.
+        I'm also able to offer commercial support.  As in contracting work. Please
+        contact me at info at mvantellingen.nl for more information. If you just have a
+        random question and don't intent to actually pay me for my support then please
+        DO NOT email me at that e-mail address but just use stackoverflow or something..
         
         .. _let me know: https://github.com/mvantellingen/python-zeep/issues
         
diff --git a/src/zeep.egg-info/SOURCES.txt b/src/zeep.egg-info/SOURCES.txt
new file mode 100644
index 0000000..6f425bb
--- /dev/null
+++ b/src/zeep.egg-info/SOURCES.txt
@@ -0,0 +1,110 @@
+CHANGES
+LICENSE
+README.rst
+setup.cfg
+setup.py
+src/zeep/__init__.py
+src/zeep/__main__.py
+src/zeep/cache.py
+src/zeep/client.py
+src/zeep/exceptions.py
+src/zeep/helpers.py
+src/zeep/parser.py
+src/zeep/plugins.py
+src/zeep/transports.py
+src/zeep/utils.py
+src/zeep/wsa.py
+src/zeep.egg-info/PKG-INFO
+src/zeep.egg-info/SOURCES.txt
+src/zeep.egg-info/dependency_links.txt
+src/zeep.egg-info/not-zip-safe
+src/zeep.egg-info/pbr.json
+src/zeep.egg-info/requires.txt
+src/zeep.egg-info/top_level.txt
+src/zeep/asyncio/__init__.py
+src/zeep/asyncio/bindings.py
+src/zeep/asyncio/transport.py
+src/zeep/wsdl/__init__.py
+src/zeep/wsdl/definitions.py
+src/zeep/wsdl/parse.py
+src/zeep/wsdl/utils.py
+src/zeep/wsdl/wsdl.py
+src/zeep/wsdl/bindings/__init__.py
+src/zeep/wsdl/bindings/http.py
+src/zeep/wsdl/bindings/soap.py
+src/zeep/wsdl/messages/__init__.py
+src/zeep/wsdl/messages/base.py
+src/zeep/wsdl/messages/http.py
+src/zeep/wsdl/messages/mime.py
+src/zeep/wsdl/messages/soap.py
+src/zeep/wsse/__init__.py
+src/zeep/wsse/username.py
+src/zeep/wsse/utils.py
+src/zeep/xsd/__init__.py
+src/zeep/xsd/builtins.py
+src/zeep/xsd/const.py
+src/zeep/xsd/context.py
+src/zeep/xsd/elements.py
+src/zeep/xsd/indicators.py
+src/zeep/xsd/parser.py
+src/zeep/xsd/printer.py
+src/zeep/xsd/schema.py
+src/zeep/xsd/types.py
+src/zeep/xsd/utils.py
+src/zeep/xsd/valueobjects.py
+src/zeep/xsd/visitor.py
+tests/__init__.py
+tests/conftest.py
+tests/test_cache.py
+tests/test_client.py
+tests/test_client_factory.py
+tests/test_helpers.py
+tests/test_main.py
+tests/test_pprint.py
+tests/test_response.py
+tests/test_transports.py
+tests/test_wsa.py
+tests/test_wsdl.py
+tests/test_wsdl_arrays.py
+tests/test_wsdl_messages_document.py
+tests/test_wsdl_messages_http.py
+tests/test_wsdl_messages_rpc.py
+tests/test_wsdl_soap.py
+tests/test_wsse_username.py
+tests/test_wsse_utils.py
+tests/test_xsd.py
+tests/test_xsd_any.py
+tests/test_xsd_attributes.py
+tests/test_xsd_builtins.py
+tests/test_xsd_choice.py
+tests/test_xsd_complex_types.py
+tests/test_xsd_extension.py
+tests/test_xsd_integration.py
+tests/test_xsd_parse.py
+tests/test_xsd_schemas.py
+tests/test_xsd_signatures.py
+tests/test_xsd_simple_types.py
+tests/test_xsd_types.py
+tests/test_xsd_union.py
+tests/test_xsd_valueobjects.py
+tests/test_xsd_visitor.py
+tests/utils.py
+tests/integration/hello_world_recursive.wsdl
+tests/integration/hello_world_recursive_import.wsdl
+tests/integration/recursive_schema_a.xsd
+tests/integration/recursive_schema_b.xsd
+tests/integration/recursive_schema_c.xsd
+tests/integration/recursive_schema_main.wsdl
+tests/integration/test_hello_world_recursive.py
+tests/integration/test_http_post.py
+tests/integration/test_http_post.wsdl
+tests/integration/test_recursive_schema.py
+tests/wsdl_files/http.wsdl
+tests/wsdl_files/soap-enc.xsd
+tests/wsdl_files/soap.wsdl
+tests/wsdl_files/soap_header.wsdl
+tests/wsdl_files/soap_import_2.wsdl
+tests/wsdl_files/soap_import_main.wsdl
+tests/wsdl_files/soap_transport_err.wsdl
+tests/wsdl_files/test_import_2.xsd
+tests/wsdl_files/xmldsig-core-schema.xsd
\ No newline at end of file
diff --git a/src/zeep.egg-info/dependency_links.txt b/src/zeep.egg-info/dependency_links.txt
new file mode 100644
index 0000000..8b13789
--- /dev/null
+++ b/src/zeep.egg-info/dependency_links.txt
@@ -0,0 +1 @@
+
diff --git a/src/zeep.egg-info/not-zip-safe b/src/zeep.egg-info/not-zip-safe
new file mode 100644
index 0000000..8b13789
--- /dev/null
+++ b/src/zeep.egg-info/not-zip-safe
@@ -0,0 +1 @@
+
diff --git a/src/zeep.egg-info/pbr.json b/src/zeep.egg-info/pbr.json
new file mode 100644
index 0000000..84a528f
--- /dev/null
+++ b/src/zeep.egg-info/pbr.json
@@ -0,0 +1 @@
+{"is_release": false, "git_version": "70e9199"}
\ No newline at end of file
diff --git a/src/zeep.egg-info/requires.txt b/src/zeep.egg-info/requires.txt
new file mode 100644
index 0000000..b63991d
--- /dev/null
+++ b/src/zeep.egg-info/requires.txt
@@ -0,0 +1,23 @@
+appdirs>=1.4.0
+cached-property>=1.0.0
+defusedxml>=0.4.1
+isodate>=0.5.4
+lxml>=3.0.0
+requests>=2.7.0
+six>=1.9.0
+pytz
+
+[docs]
+sphinx>=1.4.0
+
+[test]
+freezegun==0.3.7
+mock==2.0.0
+pretend==1.0.8
+pytest-cov==2.3.1
+pytest==3.0.2
+requests_mock>=0.7.0
+isort==4.2.5
+flake8==3.0.3
+flake8-blind-except==0.1.1
+flake8-debugger==1.4.0
diff --git a/src/zeep.egg-info/top_level.txt b/src/zeep.egg-info/top_level.txt
new file mode 100644
index 0000000..6c16f20
--- /dev/null
+++ b/src/zeep.egg-info/top_level.txt
@@ -0,0 +1 @@
+zeep
diff --git a/src/zeep/__init__.py b/src/zeep/__init__.py
index 606d054..22d0183 100644
--- a/src/zeep/__init__.py
+++ b/src/zeep/__init__.py
@@ -2,4 +2,4 @@ from zeep.client import Client  # noqa
 from zeep.transports import Transport  # noqa
 from zeep.plugins import Plugin  # noqa
 
-__version__ = '0.23.0'
+__version__ = '0.24.0'
diff --git a/src/zeep/helpers.py b/src/zeep/helpers.py
index 27df819..eec2e69 100644
--- a/src/zeep/helpers.py
+++ b/src/zeep/helpers.py
@@ -1,5 +1,7 @@
 from collections import OrderedDict
 
+from lxml import etree
+
 from zeep.xsd.valueobjects import CompoundValue
 
 
@@ -8,6 +10,9 @@ def serialize_object(obj):
     if obj is None:
         return obj
 
+    if isinstance(obj, etree._Element):
+        return obj
+
     if isinstance(obj, list):
         return [serialize_object(sub) for sub in obj]
 
diff --git a/src/zeep/wsdl/bindings/soap.py b/src/zeep/wsdl/bindings/soap.py
index b700434..2f86319 100644
--- a/src/zeep/wsdl/bindings/soap.py
+++ b/src/zeep/wsdl/bindings/soap.py
@@ -193,7 +193,9 @@ class SoapBinding(Binding):
         soap_node = xmlelement.find('soap:binding', namespaces=cls.nsmap)
         transport = soap_node.get('transport')
         if transport != 'http://schemas.xmlsoap.org/soap/http':
-            raise NotImplementedError("Only soap/http is supported for now")
+            raise NotImplementedError(
+                "The binding transport %s is not supported (only soap/http)" % (
+                    transport))
         default_style = soap_node.get('style', 'document')
 
         obj = cls(definitions.wsdl, name, port_name, transport, default_style)
diff --git a/src/zeep/wsdl/messages/soap.py b/src/zeep/wsdl/messages/soap.py
index b1b64b6..3875ecf 100644
--- a/src/zeep/wsdl/messages/soap.py
+++ b/src/zeep/wsdl/messages/soap.py
@@ -288,6 +288,8 @@ class SoapMessage(ConcreteMessage):
             for header_value in headers_value:
                 if hasattr(header_value, '_xsd_elm'):
                     header_value._xsd_elm.render(header, header_value)
+                elif hasattr(header_value, '_xsd_type'):
+                    header_value._xsd_type.render(header, header_value)
                 elif isinstance(header_value, etree._Element):
                     header.append(header_value)
                 else:
@@ -298,7 +300,7 @@ class SoapMessage(ConcreteMessage):
                     "_soapheaders only accepts a dictionary if the wsdl "
                     "defines the headers.")
             headers_value = self.header(**headers_value)
-            self.header.render(header, headers_value)
+            self.header.type.render(header, headers_value)
         else:
             raise ValueError("Invalid value given to _soapheaders")
 
@@ -315,9 +317,11 @@ class SoapMessage(ConcreteMessage):
         return {}
 
     def _resolve_header(self, info, definitions, parts):
+        name = etree.QName(self.nsmap['soap-env'], 'Header')
+
         sequence = xsd.Sequence()
         if not info:
-            return xsd.Element(None, xsd.ComplexType(sequence))
+            return xsd.Element(name, xsd.ComplexType(sequence))
 
         for item in info:
             message_name = item['message'].text
@@ -334,7 +338,7 @@ class SoapMessage(ConcreteMessage):
             else:
                 element = xsd.Element(part_name, part.type)
             sequence.append(element)
-        return xsd.Element(None, xsd.ComplexType(sequence))
+        return xsd.Element(name, xsd.ComplexType(sequence))
 
 
 class DocumentMessage(SoapMessage):
@@ -360,8 +364,10 @@ class DocumentMessage(SoapMessage):
         return {'body': result}
 
     def _resolve_body(self, info, definitions, parts):
+        name = etree.QName(self.nsmap['soap-env'], 'Body')
+
         if not info or not parts:
-            return xsd.Element(None, xsd.ComplexType([]))
+            return xsd.Element(name, xsd.ComplexType([]))
 
         # If the part name is omitted then all parts are available under
         # the soap:body tag. Otherwise only the part with the given name.
@@ -377,8 +383,7 @@ class DocumentMessage(SoapMessage):
 
         if len(sub_elements) > 1:
             self._is_body_wrapped = True
-            return xsd.Element(
-                None, xsd.ComplexType(xsd.All(sub_elements)))
+            return xsd.Element(name, xsd.ComplexType(xsd.All(sub_elements)))
         else:
             self._is_body_wrapped = False
             return sub_elements[0]
@@ -404,8 +409,9 @@ class RpcMessage(SoapMessage):
         name and its namespace is the value of the namespace attribute.
 
         """
+        name = etree.QName(self.nsmap['soap-env'], 'Body')
         if not info:
-            return xsd.Element(None, xsd.ComplexType([]))
+            return xsd.Element(name, xsd.ComplexType([]))
 
         namespace = info['namespace']
         if self.type == 'input':
diff --git a/src/zeep/wsdl/wsdl.py b/src/zeep/wsdl/wsdl.py
index 3eb247c..ce14b66 100644
--- a/src/zeep/wsdl/wsdl.py
+++ b/src/zeep/wsdl/wsdl.py
@@ -358,7 +358,12 @@ class Definition(object):
             binding = None
             for binding_class in binding_classes:
                 if binding_class.match(binding_node):
-                    binding = binding_class.parse(self, binding_node)
+
+                    try:
+                        binding = binding_class.parse(self, binding_node)
+                    except NotImplementedError as exc:
+                        logger.debug("Ignoring binding: %s", exc)
+                        continue
 
                     logger.debug("Adding binding: %s", binding.name.text)
                     result[binding.name.text] = binding
diff --git a/src/zeep/xsd/builtins.py b/src/zeep/xsd/builtins.py
index 5219ae5..a4b64f3 100644
--- a/src/zeep/xsd/builtins.py
+++ b/src/zeep/xsd/builtins.py
@@ -179,6 +179,8 @@ class DateTime(_BuiltinType):
 
     @check_no_collection
     def xmlvalue(self, value):
+        if value.microsecond:
+            return isodate.isostrf.strftime(value, '%Y-%m-%dT%H:%M:%S.%f%Z')
         return isodate.isostrf.strftime(value, '%Y-%m-%dT%H:%M:%S%Z')
 
     def pythonvalue(self, value):
@@ -191,6 +193,8 @@ class Time(_BuiltinType):
 
     @check_no_collection
     def xmlvalue(self, value):
+        if value.microsecond:
+            return isodate.isostrf.strftime(value, '%H:%M:%S.%f%Z')
         return isodate.isostrf.strftime(value, '%H:%M:%S%Z')
 
     def pythonvalue(self, value):
@@ -512,6 +516,9 @@ class AnyType(_BuiltinType):
         if isinstance(value, AnyObject):
             value.xsd_type.render(parent, value.value)
             parent.set(xsi_ns('type'), value.xsd_type.qname)
+        elif hasattr(value, '_xsd_elm'):
+            value._xsd_elm.render(parent, value)
+            parent.set(xsi_ns('type'), value._xsd_elm.qname)
         else:
             parent.text = self.xmlvalue(value)
 
@@ -525,7 +532,12 @@ class AnyType(_BuiltinType):
             return None
 
         if xsi_type and schema:
-            xsd_type = schema.get_type(xsi_type)
+            xsd_type = schema.get_type(xsi_type, fail_silently=True)
+
+            # If we were unable to resolve a type for the xsi:type (due to
+            # buggy soap servers) then we just return the lxml element.
+            if not xsd_type:
+                return xmlelement.getchildren()
 
             # If the xsd_type is xsd:anyType then we will recurs so ignore
             # that.
diff --git a/src/zeep/xsd/elements.py b/src/zeep/xsd/elements.py
index e7e3d87..8a62188 100644
--- a/src/zeep/xsd/elements.py
+++ b/src/zeep/xsd/elements.py
@@ -28,13 +28,14 @@ class Base(object):
     def is_optional(self):
         return self.min_occurs == 0
 
-    def parse_args(self, args):
+    def parse_args(self, args, index=0):
         result = {}
         if not args:
-            return result, args
+            return result, args, index
 
-        value = args.pop(0)
-        return {self.attr_name: value}, args
+        value = args[index]
+        index += 1
+        return {self.attr_name: value}, args, index
 
     def parse_kwargs(self, kwargs, name, available_kwargs):
         raise NotImplementedError()
@@ -146,7 +147,7 @@ class Any(Base):
             self._render_value_item(parent, value)
 
     def _render_value_item(self, parent, value):
-        if not value:
+        if value is None:  # can be an lxml element
             return
 
         # Check if we received a proper value object. If we receive the wrong
@@ -202,7 +203,9 @@ class Any(Base):
 class Element(Base):
     def __init__(self, name, type_=None, min_occurs=1, max_occurs=1,
                  nillable=False, default=None, is_global=False, attr_name=None):
-        if name and not isinstance(name, etree.QName):
+        if name is None:
+            raise ValueError("name cannot be None", self.__class__)
+        if not isinstance(name, etree.QName):
             name = etree.QName(name)
 
         self.name = name.localname if name else None
@@ -264,10 +267,10 @@ class Element(Base):
         """
         context = context or XmlParserContext()
         instance_type = qname_attr(xmlelement, xsi_ns('type'))
+        xsd_type = None
         if instance_type:
-            xsd_type = schema.get_type(instance_type)
-        else:
-            xsd_type = self.type
+            xsd_type = schema.get_type(instance_type, fail_silently=True)
+        xsd_type = xsd_type or self.type
         return xsd_type.parse_xmlelement(
             xmlelement, schema, allow_none=allow_none, context=context)
 
@@ -343,9 +346,6 @@ class Element(Base):
                 elm.set(xsi_ns('nil'), 'true')
             return
 
-        if self.name is None:
-            return self.type.render(parent, value)
-
         node = etree.SubElement(parent, self.qname)
         xsd_type = getattr(value, '_xsd_type', self.type)
 
diff --git a/src/zeep/xsd/indicators.py b/src/zeep/xsd/indicators.py
index 2ba117c..ccfe423 100644
--- a/src/zeep/xsd/indicators.py
+++ b/src/zeep/xsd/indicators.py
@@ -97,15 +97,15 @@ class OrderIndicator(Indicator, list):
                 num += element.accept(values)
         return num
 
-    def parse_args(self, args):
+    def parse_args(self, args, index=0):
         result = {}
         for name, element in self.elements:
-            if not args:
+            if index >= len(args):
                 break
-            arg = args.pop(0)
-            result[name] = arg
+            result[name] = args[index]
+            index += 1
 
-        return result, args
+        return result, args, index
 
     def parse_kwargs(self, kwargs, name, available_kwargs):
         """Apply the given kwarg to the element.
@@ -164,12 +164,7 @@ class OrderIndicator(Indicator, list):
         return self
 
     def render(self, parent, value):
-        """Create subelements in the given parent object.
-
-        To make sure we render values only once the value items are copied
-        and the rendered attribute is removed from it once it is rendered.
-
-        """
+        """Create subelements in the given parent object."""
         if not isinstance(value, list):
             values = [value]
         else:
@@ -180,7 +175,6 @@ class OrderIndicator(Indicator, list):
                 if name:
                     if name in value:
                         element_value = value[name]
-                        del value[name]
                     else:
                         element_value = None
                 else:
@@ -256,8 +250,9 @@ class Choice(OrderIndicator):
         result = []
 
         for i in max_occurs_iter(self.max_occurs):
-            if len(xmlelements) < 1:
+            if not xmlelements:
                 break
+
             for node in list(xmlelements):
 
                 # Choose out of multiple
@@ -409,7 +404,7 @@ class Choice(OrderIndicator):
             else:
                 num = element.accept(values)
                 nums.add(num)
-        return max(nums)
+        return max(nums) if nums else 0
 
     def _find_element_to_render(self, value):
         """Return a tuple (element, value) for the best matching choice"""
@@ -460,6 +455,9 @@ class Sequence(OrderIndicator):
     def parse_xmlelements(self, xmlelements, schema, name=None, context=None):
         result = []
         for item in max_occurs_iter(self.max_occurs):
+            if not xmlelements:
+                break
+
             item_result = OrderedDict()
             for elm_name, element in self.elements:
                 item_subresult = element.parse_xmlelements(
@@ -516,8 +514,8 @@ class Group(Indicator):
             return [('_value_1', self.child)]
         return self.child.elements
 
-    def parse_args(self, args):
-        return self.child.parse_args(args)
+    def parse_args(self, args, index=0):
+        return self.child.parse_args(args, index)
 
     def parse_kwargs(self, kwargs, name, available_kwargs):
         if self.accepts_multiple:
diff --git a/src/zeep/xsd/schema.py b/src/zeep/xsd/schema.py
index 000ac95..3f9da9f 100644
--- a/src/zeep/xsd/schema.py
+++ b/src/zeep/xsd/schema.py
@@ -106,7 +106,7 @@ class Schema(object):
                 "No schema available for the namespace %r."
             ) % (qname.text, qname.namespace))
 
-    def get_type(self, qname):
+    def get_type(self, qname, fail_silently=False):
         """Return a global xsd.Type object with the given qname"""
         qname = self._create_qname(qname)
 
@@ -121,10 +121,15 @@ class Schema(object):
             schema = self._get_schema_document(qname.namespace)
             return schema.get_type(qname)
         except exceptions.NamespaceError:
-            raise exceptions.NamespaceError((
+            message = (
                 "Unable to resolve type %s. " +
                 "No schema available for the namespace %r."
-            ) % (qname.text, qname.namespace))
+            ) % (qname.text, qname.namespace)
+
+            if fail_silently:
+                logger.info(message)
+            else:
+                raise exceptions.NamespaceError(message)
 
     def get_group(self, qname):
         """Return a global xsd.Group object with the given qname"""
diff --git a/src/zeep/xsd/types.py b/src/zeep/xsd/types.py
index 7ce7522..87901b1 100644
--- a/src/zeep/xsd/types.py
+++ b/src/zeep/xsd/types.py
@@ -355,8 +355,11 @@ class ComplexType(Type):
             else:
                 element.render(parent, element_value)
 
-        if xsd_type and xsd_type._xsd_name:
-            parent.set(xsi_ns('type'), xsd_type._xsd_name)
+        if xsd_type:
+            if xsd_type._xsd_name:
+                parent.set(xsi_ns('type'), xsd_type._xsd_name)
+            if xsd_type.qname:
+                parent.set(xsi_ns('type'), xsd_type.qname)
 
     def parse_kwargs(self, kwargs, name, available_kwargs):
         value = None
diff --git a/src/zeep/xsd/valueobjects.py b/src/zeep/xsd/valueobjects.py
index 2a508df..8cd5f18 100644
--- a/src/zeep/xsd/valueobjects.py
+++ b/src/zeep/xsd/valueobjects.py
@@ -55,6 +55,13 @@ class CompoundValue(object):
     def __contains__(self, key):
         return self.__values__.__contains__(key)
 
+    def __eq__(self, other):
+        if self.__class__ != other.__class__:
+            return False
+
+        other_values = {key: other[key] for key in other}
+        return other_values == self.__values__
+
     def __len__(self):
         return self.__values__.__len__()
 
@@ -118,21 +125,24 @@ def _process_signature(xsd_type, args, kwargs):
     if args:
         args = list(args)
         num_args = len(args)
+        index = 0
 
         for element_name, element in xsd_type.elements_nested:
-            values, args = element.parse_args(args)
+            values, args, index = element.parse_args(args, index)
             if not values:
                 break
             result.update(values)
 
-    if args:
         for attribute_name, attribute in xsd_type.attributes:
-            result[attribute_name] = args.pop(0)
+            if num_args <= index:
+                break
+            result[attribute_name] = args[index]
+            index += 1
 
-    if args:
-        raise TypeError(
-            "__init__() takes at most %s positional arguments (%s given)" % (
-                len(result), num_args))
+        if num_args > index:
+            raise TypeError(
+                "__init__() takes at most %s positional arguments (%s given)" % (
+                    len(result), num_args))
 
     # Process the named arguments (sequence/group/all/choice). The
     # available_kwargs set is modified in-place.
diff --git a/tests/integration/test_http_post.py b/tests/integration/test_http_post.py
new file mode 100644
index 0000000..dfcb992
--- /dev/null
+++ b/tests/integration/test_http_post.py
@@ -0,0 +1,34 @@
+import os
+
+import pytest
+import requests_mock
+
+import zeep
+
+WSDL = os.path.join(os.path.dirname(__file__), 'test_http_post.wsdl')
+
+
+ at pytest.mark.requests
+def test_get_urlreplacement():
+    client = zeep.Client(WSDL)
+
+    with requests_mock.mock() as m:
+        m.get('http://example.com/companyinfo/o1/EUR/', text='<root>Hoi</root>')
+        result = client.service.o1('EUR')
+        assert result == 'Hoi'
+
+        history = m.request_history[0]
+        assert history._request.path_url == '/companyinfo/o1/EUR/'
+
+
+ at pytest.mark.requests
+def test_post_mime_content():
+    client = zeep.Client(WSDL, service_name='CompanyInfoService', port_name='Port3')
+
+    with requests_mock.mock() as m:
+        m.post('http://example.com/companyinfo/o1', text='<root>Hoi</root>')
+        result = client.service.o1('EUR')
+        assert result == 'Hoi'
+
+        history = m.request_history[0]
+        assert history._request.path_url == '/companyinfo/o1'
diff --git a/tests/integration/test_http_post.wsdl b/tests/integration/test_http_post.wsdl
new file mode 100644
index 0000000..5a33e1c
--- /dev/null
+++ b/tests/integration/test_http_post.wsdl
@@ -0,0 +1,74 @@
+<?xml version="1.0"?>
+<definitions 
+  xmlns="http://schemas.xmlsoap.org/wsdl/"
+  xmlns:http="http://schemas.xmlsoap.org/wsdl/http/" 
+  xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+  xmlns:mime="http://schemas.xmlsoap.org/wsdl/mime/" 
+  xmlns:tns="http://test.python-zeep.org/tns/"
+  targetNamespace="http://test.python-zeep.org/tns/">
+  <types>
+    <xsd:schema></xsd:schema>
+  </types>
+  <message name="m1">
+    <part name="tickerSymbol" type="xsd:string"/>
+  </message>
+  <message name="m2">
+    <part name="image" type="xsd:hexBinary"/>
+  </message>
+  <portType name="pt1">
+    <operation name="o1">
+      <input message="tns:m1"/>
+      <output message="tns:m2"/>
+    </operation>
+  </portType>
+  <binding name="b1" type="pt1">
+    <http:binding verb="GET"/>
+    <operation name="o1">
+      <http:operation location="/o1/(tickerSymbol)/"/>
+      <input>
+        <http:urlReplacement/>
+      </input>
+      <output>
+        <mime:content type="image/gif"/>
+        <mime:content type="image/jpeg"/>
+      </output>
+    </operation>
+  </binding>
+  <binding name="b2" type="pt1">
+    <http:binding verb="GET"/>
+    <operation name="o1">
+      <http:operation location="/o1"/>
+      <input>
+        <http:urlEncoded/>
+      </input>
+      <output>
+        <mime:content type="image/gif"/>
+        <mime:content type="image/jpeg"/>
+      </output>
+    </operation>
+  </binding>
+  <binding name="b3" type="pt1">
+    <http:binding verb="POST"/>
+    <operation name="o1">
+      <http:operation location="/o1"/>
+      <input>
+        <mime:content type="application/x-www-form-urlencoded"/>
+      </input>
+      <output>
+        <mime:content type="image/gif"/>
+        <mime:content type="image/jpeg"/>
+      </output>
+    </operation>
+  </binding>
+  <service name="CompanyInfoService">
+    <port name="Port1" binding="tns:b1">
+      <http:address location="http://example.com/companyinfo"/>
+    </port>
+    <port name="Port2" binding="tns:b2">
+      <http:address location="http://example.com/companyinfo"/>
+    </port>
+    <port name="Port3" binding="tns:b3">
+      <http:address location="http://example.com/companyinfo"/>
+    </port>
+  </service>
+</definitions>
diff --git a/tests/test_client.py b/tests/test_client.py
index 122abc3..19dfb5c 100644
--- a/tests/test_client.py
+++ b/tests/test_client.py
@@ -16,6 +16,12 @@ def test_bind():
     assert service
 
 
+def test_unknown_transport():
+    client_obj = client.Client('tests/wsdl_files/soap_transport_err.wsdl')
+    service = client_obj.bind()
+    assert service
+
+
 def test_bind_service():
     client_obj = client.Client('tests/wsdl_files/soap.wsdl')
     service = client_obj.bind('StockQuoteService')
@@ -174,13 +180,13 @@ def test_set_context_options_timeout():
 
 @pytest.mark.requests
 def test_default_soap_headers():
-    header = xsd.Element(None, xsd.ComplexType(
+    header = xsd.ComplexType(
         xsd.Sequence([
             xsd.Element('{http://tests.python-zeep.org}name', xsd.String()),
             xsd.Element('{http://tests.python-zeep.org}password', xsd.String()),
         ])
-    ))
-    header_value = header(name='ik', password='geheim')
+    )
+    header_value = header(name='ik', password='foo')
 
     client_obj = client.Client('tests/wsdl_files/soap.wsdl')
     client_obj.set_default_soapheaders([header_value])
@@ -211,20 +217,20 @@ def test_default_soap_headers():
 
 @pytest.mark.requests
 def test_default_soap_headers_extra():
-    header = xsd.Element(None, xsd.ComplexType(
+    header = xsd.ComplexType(
         xsd.Sequence([
             xsd.Element('{http://tests.python-zeep.org}name', xsd.String()),
             xsd.Element('{http://tests.python-zeep.org}password', xsd.String()),
         ])
-    ))
+    )
     header_value = header(name='ik', password='geheim')
 
-    extra_header = xsd.Element(None, xsd.ComplexType(
+    extra_header = xsd.ComplexType(
         xsd.Sequence([
             xsd.Element('{http://tests.python-zeep.org}name', xsd.String()),
             xsd.Element('{http://tests.python-zeep.org}password', xsd.String()),
         ])
-    ))
+    )
     extra_header_value = extra_header(name='ik', password='geheim')
 
     client_obj = client.Client('tests/wsdl_files/soap.wsdl')
diff --git a/tests/test_client_factory.py b/tests/test_client_factory.py
new file mode 100644
index 0000000..92d5b1a
--- /dev/null
+++ b/tests/test_client_factory.py
@@ -0,0 +1,35 @@
+import pytest
+
+from zeep import Client
+
+
+def test_factory_namespace():
+    client = Client('tests/wsdl_files/soap.wsdl')
+    factory = client.type_factory('http://example.com/stockquote.xsd')
+    obj = factory.Address(NameFirst='Michael', NameLast='van Tellingen')
+    assert obj.NameFirst == 'Michael'
+    assert obj.NameLast == 'van Tellingen'
+
+
+def test_factory_ns_auto_prefix():
+    client = Client('tests/wsdl_files/soap.wsdl')
+    factory = client.type_factory('ns0')
+    obj = factory.Address(NameFirst='Michael', NameLast='van Tellingen')
+    assert obj.NameFirst == 'Michael'
+    assert obj.NameLast == 'van Tellingen'
+
+
+def test_factory_ns_custom_prefix():
+    client = Client('tests/wsdl_files/soap.wsdl')
+    client.set_ns_prefix('sq', 'http://example.com/stockquote.xsd')
+    factory = client.type_factory('sq')
+    obj = factory.Address(NameFirst='Michael', NameLast='van Tellingen')
+    assert obj.NameFirst == 'Michael'
+    assert obj.NameLast == 'van Tellingen'
+
+
+def test_factory_ns_unknown_prefix():
+    client = Client('tests/wsdl_files/soap.wsdl')
+
+    with pytest.raises(ValueError):
+        client.type_factory('bla')
diff --git a/tests/test_helpers.py b/tests/test_helpers.py
index a3ddb06..7165a5f 100644
--- a/tests/test_helpers.py
+++ b/tests/test_helpers.py
@@ -1,6 +1,6 @@
 from lxml import etree
 
-from tests.utils import load_xml
+from tests.utils import assert_nodes_equal, load_xml
 from zeep import xsd
 from zeep.helpers import serialize_object
 
@@ -111,3 +111,39 @@ def test_nested_complex_types():
     assert isinstance(result, dict), type(result)
     assert isinstance(result['item'], dict), type(result['item'])
     assert result['item']['item_1'] == 'foo'
+
+
+def test_serialize_any_array():
+    custom_type = xsd.Element(
+        etree.QName('http://tests.python-zeep.org/', 'authentication'),
+        xsd.ComplexType(
+            xsd.Sequence([
+                xsd.Any(max_occurs=2),
+            ])
+        ))
+
+    any_obj = etree.Element('{http://tests.python-zeep.org}lxml')
+    etree.SubElement(any_obj, 'node').text = 'foo'
+
+    obj = custom_type(any_obj)
+
+    expected = """
+        <document>
+          <ns0:authentication xmlns:ns0="http://tests.python-zeep.org/">
+            <ns0:lxml xmlns:ns0="http://tests.python-zeep.org">
+              <node>foo</node>
+            </ns0:lxml>
+          </ns0:authentication>
+        </document>
+    """
+    node = etree.Element('document')
+    custom_type.render(node, obj)
+    assert_nodes_equal(expected, node)
+
+    schema = xsd.Schema()
+    obj = custom_type.parse(node.getchildren()[0], schema=schema)
+    result = serialize_object(obj)
+
+    assert result == {
+        '_value_1': [any_obj],
+    }
diff --git a/tests/test_main.py b/tests/test_main.py
new file mode 100644
index 0000000..de98d04
--- /dev/null
+++ b/tests/test_main.py
@@ -0,0 +1,31 @@
+from mock import patch
+from pretend import stub
+
+from zeep import __main__, client
+
+
+def test_main_no_args(monkeypatch):
+    def mock_init(self, *args, **kwargs):
+        self.wsdl = stub(dump=lambda: None)
+
+    monkeypatch.setattr(client.Client, '__init__', mock_init)
+    args = __main__.parse_arguments(['foo.wsdl'])
+    __main__.main(args)
+
+
+def test_main_extract_auth(monkeypatch):
+    def mock_init(self, *args, **kwargs):
+        self.wsdl = stub(dump=lambda: None)
+
+    monkeypatch.setattr(client.Client, '__init__', mock_init)
+
+    with patch.object(__main__, 'Transport', autospec=True) as mock_transport:
+        args = __main__.parse_arguments(
+            ['http://user:secret@tests.python-zeep.org/foo.wsdl'])
+
+        __main__.main(args)
+
+        assert mock_transport.call_count == 1
+
+        args, kwargs = mock_transport.call_args
+        assert kwargs['http_auth'] == ('user', 'secret')
diff --git a/tests/test_pprint.py b/tests/test_pprint.py
new file mode 100644
index 0000000..1c53780
--- /dev/null
+++ b/tests/test_pprint.py
@@ -0,0 +1,34 @@
+from zeep.xsd import printer
+
+
+def test_dict():
+    pprint = printer.PrettyPrinter()
+    data = {
+        'foo': 'bar',
+        'foo_2': 'bar',
+        'foo_3': 'bar',
+        'foo_4': {
+            'bar': '1',
+            'bar': {
+                'bala': 'qwe',
+            },
+            'x': [1, 2, 3, 4],
+            'y': [],
+        }
+    }
+    pprint.pformat(data)
+
+
+def test_list():
+    pprint = printer.PrettyPrinter()
+    data = [
+        {
+            'foo': 'bar',
+            'foo_2': 'bar',
+        },
+        {
+            'foo': 'bar',
+            'foo_2': 'bar',
+        },
+    ]
+    pprint.pformat(data)
diff --git a/tests/test_transports.py b/tests/test_transports.py
new file mode 100644
index 0000000..63f3e00
--- /dev/null
+++ b/tests/test_transports.py
@@ -0,0 +1,29 @@
+import pytest
+import requests_mock
+from pretend import stub
+
+from zeep import cache, transports
+
+
+ at pytest.mark.requests
+def test_default_cache():
+    transport = transports.Transport()
+    assert isinstance(transport.cache, cache.SqliteCache)
+
+
+ at pytest.mark.requests
+def test_no_cache():
+    transport = transports.Transport(cache=None)
+    assert transport.cache is None
+
+
+ at pytest.mark.requests
+def test_load():
+    cache = stub(get=lambda url: None, add=lambda url, content: None)
+    transport = transports.Transport(cache=cache)
+
+    with requests_mock.mock() as m:
+        m.get('http://tests.python-zeep.org/test.xml', text='x')
+        result = transport.load('http://tests.python-zeep.org/test.xml')
+
+        assert result == b'x'
diff --git a/tests/test_wsa.py b/tests/test_wsa.py
new file mode 100644
index 0000000..a53d990
--- /dev/null
+++ b/tests/test_wsa.py
@@ -0,0 +1,268 @@
+import uuid
+
+from pretend import stub
+from six import StringIO
+
+from tests.utils import DummyTransport, assert_nodes_equal
+from zeep import wsa, wsdl, Client
+
+
+def test_require_wsa(recwarn, monkeypatch):
+    monkeypatch.setattr(
+        uuid, 'uuid4', lambda: uuid.UUID('ada686f9-5995-4088-bea4-239f694b2eaf'))
+
+    wsdl_main = StringIO("""
+        <?xml version="1.0"?>
+        <wsdl:definitions
+          xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
+          xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+          xmlns:tns="http://tests.python-zeep.org/xsd-main"
+          xmlns:sec="http://tests.python-zeep.org/wsdl-secondary"
+          xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
+          xmlns:wsaw="http://www.w3.org/2006/05/addressing/wsdl"
+          xmlns:wsdlsoap="http://schemas.xmlsoap.org/wsdl/soap/"
+          targetNamespace="http://tests.python-zeep.org/xsd-main">
+          <wsdl:types>
+            <xsd:schema
+                targetNamespace="http://tests.python-zeep.org/xsd-main"
+                xmlns:tns="http://tests.python-zeep.org/xsd-main">
+              <xsd:element name="input" type="xsd:string"/>
+              <xsd:element name="input2" type="xsd:string"/>
+            </xsd:schema>
+          </wsdl:types>
+
+          <wsdl:message name="dummyRequest">
+            <wsdl:part name="response" element="tns:input"/>
+          </wsdl:message>
+          <wsdl:message name="dummyResponse">
+            <wsdl:part name="response" element="tns:input2"/>
+          </wsdl:message>
+
+          <wsdl:portType name="TestPortType">
+            <wsdl:operation name="TestOperation1">
+              <wsdl:input message="dummyRequest" wsaw:Action="urn:dummyRequest"/>
+              <wsdl:output message="dummyResponse"  wsaw:Action="urn:dummyResponse"/>
+            </wsdl:operation>
+          </wsdl:portType>
+
+          <wsdl:binding name="TestBinding" type="tns:TestPortType">
+            <soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http"/>
+            <wsdl:operation name="TestOperation1">
+              <soap:operation soapAction=""/>
+              <wsdl:input>
+                <soap:body use="literal"/>
+              </wsdl:input>
+              <wsdl:output>
+                <soap:body use="literal"/>
+              </wsdl:output>
+            </wsdl:operation>
+          </wsdl:binding>
+          <wsdl:service name="TestService">
+            <wsdl:documentation>Test service</wsdl:documentation>
+            <wsdl:port name="TestPortType" binding="tns:TestBinding">
+              <soap:address location="http://tests.python-zeep.org/test"/>
+            </wsdl:port>
+          </wsdl:service>
+        </wsdl:definitions>
+    """.strip())
+
+    client = stub(plugins=[], wsse=None)
+
+    transport = DummyTransport()
+    client = Client(wsdl_main, transport=transport)
+    binding = client.wsdl.services.get('TestService').ports.get('TestPortType').binding
+
+    envelope, headers = binding._create(
+        'TestOperation1',
+        args=['foo'],
+        kwargs={},
+        client=client,
+        options={'address': 'http://tests.python-zeep.org/test'})
+    expected = """
+        <soap-env:Envelope
+            xmlns:soap-env="http://schemas.xmlsoap.org/soap/envelope/">
+          <soap-env:Header  xmlns:wsa="http://www.w3.org/2005/08/addressing">
+            <wsa:Action>urn:dummyRequest</wsa:Action>
+            <wsa:MessageID>urn:uuid:ada686f9-5995-4088-bea4-239f694b2eaf</wsa:MessageID>
+            <wsa:To>http://tests.python-zeep.org/test</wsa:To>
+          </soap-env:Header>
+          <soap-env:Body>
+            <ns0:input xmlns:ns0="http://tests.python-zeep.org/xsd-main">foo</ns0:input>
+          </soap-env:Body>
+        </soap-env:Envelope>
+    """
+    assert_nodes_equal(expected, envelope)
+
+
+def test_force_wsa(recwarn, monkeypatch):
+    monkeypatch.setattr(
+        uuid, 'uuid4', lambda: uuid.UUID('ada686f9-5995-4088-bea4-239f694b2eaf'))
+
+    wsdl_main = StringIO("""
+        <?xml version="1.0"?>
+        <wsdl:definitions
+          xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
+          xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+          xmlns:tns="http://tests.python-zeep.org/xsd-main"
+          xmlns:sec="http://tests.python-zeep.org/wsdl-secondary"
+          xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
+          xmlns:wsdlsoap="http://schemas.xmlsoap.org/wsdl/soap/"
+          targetNamespace="http://tests.python-zeep.org/xsd-main">
+          <wsdl:types>
+            <xsd:schema
+                targetNamespace="http://tests.python-zeep.org/xsd-main"
+                xmlns:tns="http://tests.python-zeep.org/xsd-main">
+              <xsd:element name="input" type="xsd:string"/>
+              <xsd:element name="input2" type="xsd:string"/>
+            </xsd:schema>
+          </wsdl:types>
+
+          <wsdl:message name="dummyRequest">
+            <wsdl:part name="response" element="tns:input"/>
+          </wsdl:message>
+          <wsdl:message name="dummyResponse">
+            <wsdl:part name="response" element="tns:input2"/>
+          </wsdl:message>
+
+          <wsdl:portType name="TestPortType">
+            <wsdl:operation name="TestOperation1">
+              <wsdl:input message="dummyRequest"/>
+              <wsdl:output message="dummyResponse"/>
+            </wsdl:operation>
+          </wsdl:portType>
+
+          <wsdl:binding name="TestBinding" type="tns:TestPortType">
+            <soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http"/>
+            <wsdl:operation name="TestOperation1">
+              <soap:operation soapAction="urn:dummyRequest"/>
+              <wsdl:input>
+                <soap:body use="literal"/>
+              </wsdl:input>
+              <wsdl:output>
+                <soap:body use="literal"/>
+              </wsdl:output>
+            </wsdl:operation>
+          </wsdl:binding>
+          <wsdl:service name="TestService">
+            <wsdl:documentation>Test service</wsdl:documentation>
+            <wsdl:port name="TestPortType" binding="tns:TestBinding">
+              <soap:address location="http://tests.python-zeep.org/test"/>
+            </wsdl:port>
+          </wsdl:service>
+        </wsdl:definitions>
+    """.strip())
+
+    transport = DummyTransport()
+    client = Client(wsdl_main, transport=transport, plugins=[wsa.WsAddressingPlugin()])
+    binding = client.wsdl.services.get('TestService').ports.get('TestPortType').binding
+
+    envelope, headers = binding._create(
+        'TestOperation1',
+        args=['foo'],
+        kwargs={},
+        client=client,
+        options={'address': 'http://tests.python-zeep.org/test'})
+    expected = """
+        <soap-env:Envelope
+            xmlns:soap-env="http://schemas.xmlsoap.org/soap/envelope/">
+          <soap-env:Header  xmlns:wsa="http://www.w3.org/2005/08/addressing">
+            <wsa:Action>urn:dummyRequest</wsa:Action>
+            <wsa:MessageID>urn:uuid:ada686f9-5995-4088-bea4-239f694b2eaf</wsa:MessageID>
+            <wsa:To>http://tests.python-zeep.org/test</wsa:To>
+          </soap-env:Header>
+          <soap-env:Body>
+            <ns0:input xmlns:ns0="http://tests.python-zeep.org/xsd-main">foo</ns0:input>
+          </soap-env:Body>
+        </soap-env:Envelope>
+    """
+    assert_nodes_equal(expected, envelope)
+
+
+def test_force_wsa_soap12(recwarn, monkeypatch):
+    monkeypatch.setattr(
+        uuid, 'uuid4', lambda: uuid.UUID('ada686f9-5995-4088-bea4-239f694b2eaf'))
+
+    wsdl_main = StringIO("""
+        <?xml version="1.0"?>
+        <wsdl:definitions
+          xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
+          xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+          xmlns:tns="http://tests.python-zeep.org/xsd-main"
+          xmlns:sec="http://tests.python-zeep.org/wsdl-secondary"
+          xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap12/"
+          xmlns:wsdlsoap="http://schemas.xmlsoap.org/wsdl/soap12/"
+          targetNamespace="http://tests.python-zeep.org/xsd-main">
+          <wsdl:types>
+            <xsd:schema
+                targetNamespace="http://tests.python-zeep.org/xsd-main"
+                xmlns:tns="http://tests.python-zeep.org/xsd-main">
+              <xsd:element name="input" type="xsd:string"/>
+              <xsd:element name="input2" type="xsd:string"/>
+            </xsd:schema>
+          </wsdl:types>
+
+          <wsdl:message name="dummyRequest">
+            <wsdl:part name="response" element="tns:input"/>
+          </wsdl:message>
+          <wsdl:message name="dummyResponse">
+            <wsdl:part name="response" element="tns:input2"/>
+          </wsdl:message>
+
+          <wsdl:portType name="TestPortType">
+            <wsdl:operation name="TestOperation1">
+              <wsdl:input message="dummyRequest"/>
+              <wsdl:output message="dummyResponse"/>
+            </wsdl:operation>
+          </wsdl:portType>
+
+          <wsdl:binding name="TestBinding" type="tns:TestPortType">
+            <soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http"/>
+            <wsdl:operation name="TestOperation1">
+              <soap:operation soapAction="urn:dummyRequest"/>
+              <wsdl:input>
+                <soap:body use="literal"/>
+              </wsdl:input>
+              <wsdl:output>
+                <soap:body use="literal"/>
+              </wsdl:output>
+            </wsdl:operation>
+          </wsdl:binding>
+          <wsdl:service name="TestService">
+            <wsdl:documentation>Test service</wsdl:documentation>
+            <wsdl:port name="TestPortType" binding="tns:TestBinding">
+              <soap:address location="http://tests.python-zeep.org/test"/>
+            </wsdl:port>
+          </wsdl:service>
+        </wsdl:definitions>
+    """.strip())
+
+    client = stub(plugins=[wsa.WsAddressingPlugin()], wsse=None)
+
+    transport = DummyTransport()
+    doc = wsdl.Document(wsdl_main, transport)
+    binding = doc.services.get('TestService').ports.get('TestPortType').binding
+
+    envelope, headers = binding._create(
+        'TestOperation1',
+        args=['foo'],
+        kwargs={},
+        client=client,
+        options={'address': 'http://tests.python-zeep.org/test'})
+    expected = """
+        <soap-env:Envelope
+            xmlns:soap-env="http://www.w3.org/2003/05/soap-envelope">
+          <soap-env:Header  xmlns:wsa="http://www.w3.org/2005/08/addressing">
+            <wsa:Action>urn:dummyRequest</wsa:Action>
+            <wsa:MessageID>urn:uuid:ada686f9-5995-4088-bea4-239f694b2eaf</wsa:MessageID>
+            <wsa:To>http://tests.python-zeep.org/test</wsa:To>
+          </soap-env:Header>
+          <soap-env:Body>
+            <ns0:input xmlns:ns0="http://tests.python-zeep.org/xsd-main">foo</ns0:input>
+
+          </soap-env:Body>
+        </soap-env:Envelope>
+    """
+    assert_nodes_equal(expected, envelope)
+
+    assert headers['Content-Type'] == (
+        'application/soap+xml; charset=utf-8; action="urn:dummyRequest"')
diff --git a/tests/test_wsdl_arrays.py b/tests/test_wsdl_arrays.py
new file mode 100644
index 0000000..f6ad722
--- /dev/null
+++ b/tests/test_wsdl_arrays.py
@@ -0,0 +1,360 @@
+import io
+
+from lxml import etree
+
+from tests.utils import DummyTransport, assert_nodes_equal, load_xml
+from zeep import xsd
+
+
+def get_transport():
+    transport = DummyTransport()
+    transport.bind(
+        'http://schemas.xmlsoap.org/soap/encoding/',
+        load_xml(io.open('tests/wsdl_files/soap-enc.xsd', 'r').read().encode('utf-8')))
+    return transport
+
+
+def test_simple_type():
+    schema = xsd.Schema(load_xml("""
+    <xsd:schema
+        xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+        xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
+        xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/"
+        targetNamespace="http://tests.python-zeep.org/tns">
+      <xsd:import namespace="http://schemas.xmlsoap.org/soap/encoding/"/>
+      <xsd:complexType name="ArrayOfString">
+        <xsd:complexContent>
+          <xsd:restriction base="SOAP-ENC:Array">
+            <xsd:attribute ref="SOAP-ENC:arrayType" wsdl:arrayType="xsd:string[]"/>
+          </xsd:restriction>
+        </xsd:complexContent>
+      </xsd:complexType>
+    </xsd:schema>
+    """), transport=get_transport())
+
+    ArrayOfString = schema.get_type('ns0:ArrayOfString')
+    print(ArrayOfString.__dict__)
+
+    value = ArrayOfString(['item', 'and', 'even', 'more', 'items'])
+
+    node = etree.Element('document')
+    ArrayOfString.render(node, value)
+
+    expected = """
+        <document>
+            <item xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="xs:string">item</item>
+            <item xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="xs:string">and</item>
+            <item xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="xs:string">even</item>
+            <item xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="xs:string">more</item>
+            <item xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="xs:string">items</item>
+        </document>
+    """  # noqa
+
+    assert_nodes_equal(expected, node)
+
+
+def test_complex_type():
+    schema = xsd.Schema(load_xml("""
+    <xsd:schema
+        xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+        xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
+        xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/"
+        xmlns:tns="http://tests.python-zeep.org/tns"
+        targetNamespace="http://tests.python-zeep.org/tns">
+      <xsd:import namespace="http://schemas.xmlsoap.org/soap/encoding/"/>
+
+      <xsd:complexType name="ArrayObject">
+        <xsd:sequence>
+          <xsd:element name="attr_1" type="xsd:string"/>
+          <xsd:element name="attr_2" type="xsd:string"/>
+        </xsd:sequence>
+      </xsd:complexType>
+      <xsd:complexType name="ArrayOfObject">
+        <xsd:complexContent>
+          <xsd:restriction base="SOAP-ENC:Array">
+            <xsd:sequence>
+              <xsd:element name="obj" type="tns:ArrayObject" minOccurs="0" maxOccurs="unbounded"/>
+            </xsd:sequence>
+            <xsd:attribute ref="SOAP-ENC:arrayType" wsdl:arrayType="tns:ArrayObject[]"/>
+          </xsd:restriction>
+        </xsd:complexContent>
+      </xsd:complexType>
+    </xsd:schema>
+    """), transport=get_transport())
+
+    ArrayOfObject = schema.get_type('ns0:ArrayOfObject')
+    ArrayObject = schema.get_type('ns0:ArrayObject')
+
+    value = ArrayOfObject([
+        ArrayObject(attr_1='attr-1', attr_2='attr-2'),
+        ArrayObject(attr_1='attr-3', attr_2='attr-4'),
+        ArrayObject(attr_1='attr-5', attr_2='attr-6'),
+    ])
+
+    node = etree.Element('document')
+    ArrayOfObject.render(node, value)
+
+    expected = """
+        <document>
+            <obj>
+                <attr_1>attr-1</attr_1>
+                <attr_2>attr-2</attr_2>
+            </obj>
+            <obj>
+                <attr_1>attr-3</attr_1>
+                <attr_2>attr-4</attr_2>
+            </obj>
+            <obj>
+                <attr_1>attr-5</attr_1>
+                <attr_2>attr-6</attr_2>
+            </obj>
+        </document>
+    """
+    assert_nodes_equal(expected, node)
+
+
+def test_complex_type_without_name():
+    schema = xsd.Schema(load_xml("""
+    <xsd:schema
+        xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+        xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
+        xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/"
+        xmlns:tns="http://tests.python-zeep.org/tns"
+        targetNamespace="http://tests.python-zeep.org/tns">
+      <xsd:import namespace="http://schemas.xmlsoap.org/soap/encoding/"/>
+
+      <xsd:complexType name="ArrayObject">
+        <xsd:sequence>
+          <xsd:element name="attr_1" type="xsd:string"/>
+          <xsd:element name="attr_2" type="xsd:string"/>
+        </xsd:sequence>
+      </xsd:complexType>
+      <xsd:complexType name="ArrayOfObject">
+        <xsd:complexContent>
+          <xsd:restriction base="SOAP-ENC:Array">
+            <xsd:attribute ref="SOAP-ENC:arrayType" wsdl:arrayType="tns:ArrayObject[]"/>
+          </xsd:restriction>
+        </xsd:complexContent>
+      </xsd:complexType>
+    </xsd:schema>
+    """), transport=get_transport())
+
+    ArrayOfObject = schema.get_type('ns0:ArrayOfObject')
+    ArrayObject = schema.get_type('ns0:ArrayObject')
+
+    value = ArrayOfObject([
+        ArrayObject(attr_1='attr-1', attr_2='attr-2'),
+        ArrayObject(attr_1='attr-3', attr_2='attr-4'),
+        ArrayObject(attr_1='attr-5', attr_2='attr-6'),
+    ])
+
+    node = etree.Element('document')
+    ArrayOfObject.render(node, value)
+
+    expected = """
+        <document>
+            <ArrayObject>
+                <attr_1>attr-1</attr_1>
+                <attr_2>attr-2</attr_2>
+            </ArrayObject>
+            <ArrayObject>
+                <attr_1>attr-3</attr_1>
+                <attr_2>attr-4</attr_2>
+            </ArrayObject>
+            <ArrayObject>
+                <attr_1>attr-5</attr_1>
+                <attr_2>attr-6</attr_2>
+            </ArrayObject>
+        </document>
+    """
+    assert_nodes_equal(expected, node)
+
+
+def test_soap_array_parse_remote_ns():
+    transport = DummyTransport()
+    transport.bind(
+        'http://schemas.xmlsoap.org/soap/encoding/',
+        load_xml(io.open('tests/wsdl_files/soap-enc.xsd', 'r').read().encode('utf-8')))
+
+    schema = xsd.Schema(load_xml("""
+        <?xml version="1.0"?>
+        <xsd:schema
+          xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+          xmlns:tns="http://tests.python-zeep.org/"
+          xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/"
+          xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
+          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+          targetNamespace="http://tests.python-zeep.org/"
+          elementFormDefault="qualified">
+          <xsd:import namespace="http://schemas.xmlsoap.org/soap/encoding/"/>
+          <xsd:simpleType name="CountryCodeType">
+            <xsd:restriction base="xsd:string">
+              <xsd:length value="2"/>
+              <xsd:pattern value="[a-zA-Z]{2}"/>
+            </xsd:restriction>
+          </xsd:simpleType>
+          <xsd:complexType name="CountryItemType">
+            <xsd:sequence>
+              <xsd:element name="code" type="tns:CountryCodeType"/>
+              <xsd:element name="name" type="xsd:string"/>
+            </xsd:sequence>
+          </xsd:complexType>
+          <xsd:complexType name="CountriesArrayType">
+            <xsd:complexContent>
+              <xsd:restriction base="soapenc:Array">
+                <xsd:attribute ref="soapenc:arrayType" wsdl:arrayType="tns:CountryItemType[]"/>
+              </xsd:restriction>
+            </xsd:complexContent>
+          </xsd:complexType>
+          <xsd:element name="countries" type="tns:CountriesArrayType"/>
+        </xsd:schema>
+    """), transport)
+
+    doc = load_xml("""
+      <countries
+            SOAP-ENC:arrayType="ns1:CountryItemType[1]"
+            xsi:type="ns1:CountriesArrayType"
+            xmlns:ns1="http://tests.python-zeep.org/"
+            xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+            xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/"
+          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+        <item xsi:type="ns1:CountryItemType">
+          <code xsi:type="ns1:CountryCodeType">NL</code>
+          <name xsi:type="xsd:string">The Netherlands</name>
+        </item>
+      </countries>
+    """)
+
+    elm = schema.get_element('ns0:countries')
+    data = elm.parse(doc, schema)
+
+    assert data._value_1[0].code == 'NL'
+    assert data._value_1[0].name == 'The Netherlands'
+
+
+def test_wsdl_array_type():
+    transport = DummyTransport()
+    transport.bind(
+        'http://schemas.xmlsoap.org/soap/encoding/',
+        load_xml(io.open('tests/wsdl_files/soap-enc.xsd', 'r').read().encode('utf-8')))
+
+    schema = xsd.Schema(load_xml("""
+        <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+                    xmlns:tns="http://tests.python-zeep.org/"
+                    xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/"
+                    xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
+                    targetNamespace="http://tests.python-zeep.org/"
+                    elementFormDefault="qualified">
+          <xsd:import namespace="http://schemas.xmlsoap.org/soap/encoding/"/>
+          <xsd:complexType name="array">
+            <xsd:complexContent>
+              <xsd:restriction base="SOAP-ENC:Array">
+                <xsd:attribute ref="SOAP-ENC:arrayType" wsdl:arrayType="tns:base[]"/>
+              </xsd:restriction>
+            </xsd:complexContent>
+          </xsd:complexType>
+          <xsd:complexType name="base">
+            <xsd:sequence>
+              <xsd:element minOccurs="0" name="item_1" type="xsd:string"/>
+              <xsd:element minOccurs="0" name="item_2" type="xsd:string"/>
+            </xsd:sequence>
+          </xsd:complexType>
+          <xsd:element name="array" type="tns:array"/>
+        </xsd:schema>
+    """), transport)
+    array_elm = schema.get_element('{http://tests.python-zeep.org/}array')
+
+    item_type = schema.get_type('{http://tests.python-zeep.org/}base')
+    item_1 = item_type(item_1='foo_1', item_2='bar_1')
+    item_2 = item_type(item_1='foo_2', item_2='bar_2')
+
+    # array = array_elm([
+    #     xsd.AnyObject(item_type, item_1),
+    #     xsd.AnyObject(item_type, item_2),
+    # ])
+
+    array = array_elm([item_1, item_2])
+    node = etree.Element('document')
+    assert array_elm.signature() == (
+        '_value_1: base[], arrayType: xsd:string, offset: arrayCoordinate, ' +
+        'id: xsd:ID, href: xsd:anyURI, _attr_1: {}')
+    array_elm.render(node, array)
+    expected = """
+        <document>
+            <ns0:array xmlns:ns0="http://tests.python-zeep.org/">
+                <base>
+                    <ns0:item_1>foo_1</ns0:item_1>
+                    <ns0:item_2>bar_1</ns0:item_2>
+                </base>
+                <base>
+                    <ns0:item_1>foo_2</ns0:item_1>
+                    <ns0:item_2>bar_2</ns0:item_2>
+                </base>
+            </ns0:array>
+        </document>
+    """
+    assert_nodes_equal(expected, node)
+
+
+def test_soap_array_parse():
+    transport = DummyTransport()
+    transport.bind(
+        'http://schemas.xmlsoap.org/soap/encoding/',
+        load_xml(io.open('tests/wsdl_files/soap-enc.xsd', 'r').read().encode('utf-8')))
+
+    schema = xsd.Schema(load_xml("""
+    <?xml version="1.0"?>
+    <schema xmlns="http://www.w3.org/2001/XMLSchema"
+            xmlns:tns="http://tests.python-zeep.org/"
+            xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/"
+            xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+            targetNamespace="http://tests.python-zeep.org/"
+            elementFormDefault="qualified">
+      <import namespace="http://schemas.xmlsoap.org/soap/encoding/"/>
+      <complexType name="FlagDetailsStruct">
+          <sequence>
+              <element name="Name">
+                  <simpleType>
+                      <restriction base="string">
+                          <maxLength value="512"/>
+                      </restriction>
+                  </simpleType>
+              </element>
+              <element name="Value" type="string"/>
+          </sequence>
+      </complexType>
+      <complexType name="FlagDetailsList">
+          <complexContent>
+              <restriction base="soapenc:Array">
+                  <sequence>
+                      <element
+                        name="FlagDetailsStruct" type="tns:FlagDetailsStruct"
+                        minOccurs="0" maxOccurs="unbounded"/>
+                  </sequence>
+                  <attribute ref="soapenc:arrayType" use="required"/>
+              </restriction>
+          </complexContent>
+      </complexType>
+      <element name="FlagDetailsList" type="tns:FlagDetailsList"/>
+    </schema>
+    """), transport)
+
+    doc = load_xml("""
+         <FlagDetailsList xmlns="http://tests.python-zeep.org/">
+            <FlagDetailsStruct>
+               <Name>flag1</Name>
+               <Value>value1</Value>
+            </FlagDetailsStruct>
+            <FlagDetailsStruct>
+               <Name>flag2</Name>
+               <Value>value2</Value>
+            </FlagDetailsStruct>
+         </FlagDetailsList>
+    """)
+
+    elm = schema.get_element('ns0:FlagDetailsList')
+    data = elm.parse(doc, schema)
+    assert data.FlagDetailsStruct[0].Name == 'flag1'
+    assert data.FlagDetailsStruct[0].Value == 'value1'
+    assert data.FlagDetailsStruct[1].Name == 'flag2'
+    assert data.FlagDetailsStruct[1].Value == 'value2'
diff --git a/tests/test_wsdl_messages_document.py b/tests/test_wsdl_messages_document.py
new file mode 100644
index 0000000..5aa18a7
--- /dev/null
+++ b/tests/test_wsdl_messages_document.py
@@ -0,0 +1,1271 @@
+from lxml import etree
+from six import StringIO
+
+from tests.utils import assert_nodes_equal, load_xml
+from zeep import xsd
+from zeep.wsdl import wsdl
+
+
+def test_parse():
+    wsdl_content = StringIO("""
+    <definitions xmlns="http://schemas.xmlsoap.org/wsdl/"
+                 xmlns:tns="http://tests.python-zeep.org/tns"
+                 xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
+                 xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+                 targetNamespace="http://tests.python-zeep.org/tns">
+      <types>
+        <xsd:schema targetNamespace="http://tests.python-zeep.org/tns">
+          <xsd:element name="Request" type="xsd:string"/>
+          <xsd:element name="Response" type="xsd:string"/>
+        </xsd:schema>
+      </types>
+
+      <message name="Input">
+        <part element="tns:Request"/>
+      </message>
+      <message name="Output">
+        <part element="tns:Response"/>
+      </message>
+
+      <portType name="TestPortType">
+        <operation name="TestOperation">
+          <input message="Input"/>
+          <output message="Output"/>
+        </operation>
+      </portType>
+
+      <binding name="TestBinding" type="tns:TestPortType">
+        <soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http"/>
+        <operation name="TestOperation">
+          <soap:operation soapAction=""/>
+          <input>
+            <soap:body use="literal"/>
+          </input>
+          <output>
+            <soap:body use="literal"/>
+          </output>
+        </operation>
+      </binding>
+    </definitions>
+    """.strip())
+
+    root = wsdl.Document(wsdl_content, None)
+
+    binding = root.bindings['{http://tests.python-zeep.org/tns}TestBinding']
+    operation = binding.get('TestOperation')
+
+    assert operation.input.body.signature() == 'xsd:string'
+    assert operation.input.header.signature() == ''
+    assert operation.input.envelope.signature() == 'body: xsd:string, header: {}'
+    assert operation.input.signature(as_output=False) == 'xsd:string'
+
+    assert operation.output.body.signature() == 'xsd:string'
+    assert operation.output.header.signature() == ''
+    assert operation.output.envelope.signature() == 'body: xsd:string, header: {}'
+    assert operation.output.signature(as_output=True) == 'body: xsd:string, header: {}'
+
+
+def test_empty_input_parse():
+    wsdl_content = StringIO("""
+    <definitions xmlns="http://schemas.xmlsoap.org/wsdl/"
+                 xmlns:tns="http://tests.python-zeep.org/tns"
+                 xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
+                 xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+                 targetNamespace="http://tests.python-zeep.org/tns">
+      <types>
+        <xsd:schema targetNamespace="http://tests.python-zeep.org/tns">
+          <xsd:element name="Request" type="xsd:string"/>
+          <xsd:element name="Response" type="xsd:string"/>
+        </xsd:schema>
+      </types>
+
+      <message name="Input"/>
+      <message name="Output">
+        <part element="tns:Response"/>
+      </message>
+
+      <portType name="TestPortType">
+        <operation name="TestOperation">
+          <input message="Input"/>
+          <output message="Output"/>
+        </operation>
+      </portType>
+
+      <binding name="TestBinding" type="tns:TestPortType">
+        <soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http"/>
+        <operation name="TestOperation">
+          <soap:operation soapAction=""/>
+          <input>
+            <soap:body use="literal"/>
+          </input>
+          <output>
+            <soap:body use="literal"/>
+          </output>
+        </operation>
+      </binding>
+    </definitions>
+    """.strip())
+
+    root = wsdl.Document(wsdl_content, None)
+
+    binding = root.bindings['{http://tests.python-zeep.org/tns}TestBinding']
+    operation = binding.get('TestOperation')
+
+    assert operation.input.body.signature() == ''
+    assert operation.input.header.signature() == ''
+    assert operation.input.envelope.signature() == 'body: {}, header: {}'
+    assert operation.input.signature(as_output=False) == ''
+
+
+def test_parse_with_header():
+    wsdl_content = StringIO("""
+    <definitions xmlns="http://schemas.xmlsoap.org/wsdl/"
+                 xmlns:tns="http://tests.python-zeep.org/tns"
+                 xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
+                 xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+                 targetNamespace="http://tests.python-zeep.org/tns">
+      <types>
+        <xsd:schema targetNamespace="http://tests.python-zeep.org/tns">
+          <xsd:element name="Request" type="xsd:string"/>
+          <xsd:element name="RequestHeader" type="xsd:string"/>
+          <xsd:element name="Response" type="xsd:string"/>
+          <xsd:element name="ResponseHeader" type="xsd:string"/>
+        </xsd:schema>
+      </types>
+
+      <message name="Input">
+        <part element="tns:Request"/>
+        <part name="auth" element="tns:RequestHeader"/>
+      </message>
+      <message name="Output">
+        <part element="tns:Response"/>
+        <part name="auth" element="tns:ResponseHeader"/>
+      </message>
+
+      <portType name="TestPortType">
+        <operation name="TestOperation">
+          <input message="Input"/>
+          <output message="Output"/>
+        </operation>
+      </portType>
+
+      <binding name="TestBinding" type="tns:TestPortType">
+        <soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http"/>
+        <operation name="TestOperation">
+          <soap:operation soapAction=""/>
+          <input>
+            <soap:header message="tns:Input" part="auth" use="literal" />
+            <soap:body use="literal"/>
+          </input>
+          <output>
+            <soap:header message="tns:Output" part="auth" use="literal" />
+            <soap:body use="literal"/>
+          </output>
+        </operation>
+      </binding>
+    </definitions>
+    """.strip())
+
+    root = wsdl.Document(wsdl_content, None)
+
+    binding = root.bindings['{http://tests.python-zeep.org/tns}TestBinding']
+    operation = binding.get('TestOperation')
+
+    assert operation.input.body.signature() == 'xsd:string'
+    assert operation.input.header.signature() == 'auth: RequestHeader()'
+    assert operation.input.envelope.signature() == 'body: xsd:string, header: {auth: RequestHeader()}'  # noqa
+    assert operation.input.signature(as_output=False) == 'xsd:string, _soapheaders={auth: RequestHeader()}'  # noqa
+
+    assert operation.output.body.signature() == 'xsd:string'
+    assert operation.output.header.signature() == 'auth: ResponseHeader()'
+    assert operation.output.envelope.signature() == 'body: xsd:string, header: {auth: ResponseHeader()}'  # noqa
+    assert operation.output.signature(as_output=True) == 'body: xsd:string, header: {auth: ResponseHeader()}'  # noqa
+
+
+def test_parse_with_header_type():
+    wsdl_content = StringIO("""
+    <definitions xmlns="http://schemas.xmlsoap.org/wsdl/"
+                 xmlns:tns="http://tests.python-zeep.org/tns"
+                 xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
+                 xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+                 targetNamespace="http://tests.python-zeep.org/tns">
+      <types>
+        <xsd:schema targetNamespace="http://tests.python-zeep.org/tns">
+          <xsd:element name="Request" type="xsd:string"/>
+          <xsd:simpleType name="RequestHeaderType">
+            <xsd:restriction base="xsd:string"/>
+          </xsd:simpleType>
+          <xsd:element name="Response" type="xsd:string"/>
+          <xsd:simpleType name="ResponseHeaderType">
+            <xsd:restriction base="xsd:string"/>
+          </xsd:simpleType>
+        </xsd:schema>
+      </types>
+
+      <message name="Input">
+        <part element="tns:Request"/>
+        <part name="auth" type="tns:RequestHeaderType"/>
+      </message>
+      <message name="Output">
+        <part element="tns:Response"/>
+        <part name="auth" type="tns:ResponseHeaderType"/>
+      </message>
+
+      <portType name="TestPortType">
+        <operation name="TestOperation">
+          <input message="Input"/>
+          <output message="Output"/>
+        </operation>
+      </portType>
+
+      <binding name="TestBinding" type="tns:TestPortType">
+        <soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http"/>
+        <operation name="TestOperation">
+          <soap:operation soapAction=""/>
+          <input>
+            <soap:header message="tns:Input" part="auth" use="literal" />
+            <soap:body use="literal"/>
+          </input>
+          <output>
+            <soap:header message="tns:Output" part="auth" use="literal" />
+            <soap:body use="literal"/>
+          </output>
+        </operation>
+      </binding>
+    </definitions>
+    """.strip())
+
+    root = wsdl.Document(wsdl_content, None)
+
+    binding = root.bindings['{http://tests.python-zeep.org/tns}TestBinding']
+    operation = binding.get('TestOperation')
+
+    assert operation.input.body.signature() == 'xsd:string'
+    assert operation.input.header.signature() == 'auth: RequestHeaderType'
+    assert operation.input.envelope.signature() == 'body: xsd:string, header: {auth: RequestHeaderType}'  # noqa
+    assert operation.input.signature(as_output=False) == 'xsd:string, _soapheaders={auth: RequestHeaderType}'  # noqa
+
+    assert operation.output.body.signature() == 'xsd:string'
+    assert operation.output.header.signature() == 'auth: ResponseHeaderType'
+    assert operation.output.envelope.signature() == 'body: xsd:string, header: {auth: ResponseHeaderType}'  # noqa
+    assert operation.output.signature(as_output=True) == 'body: xsd:string, header: {auth: ResponseHeaderType}'  # noqa
+
+
+
+
+
+def test_parse_with_header_other_message():
+    wsdl_content = StringIO("""
+    <definitions xmlns="http://schemas.xmlsoap.org/wsdl/"
+                 xmlns:tns="http://tests.python-zeep.org/tns"
+                 xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
+                 xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+                 targetNamespace="http://tests.python-zeep.org/tns">
+      <types>
+        <xsd:schema targetNamespace="http://tests.python-zeep.org/tns">
+          <xsd:element name="Request" type="xsd:string"/>
+          <xsd:element name="RequestHeader" type="xsd:string"/>
+        </xsd:schema>
+      </types>
+
+      <message name="InputHeader">
+        <part name="header" element="tns:RequestHeader"/>
+      </message>
+      <message name="Input">
+        <part element="tns:Request"/>
+      </message>
+
+      <portType name="TestPortType">
+        <operation name="TestOperation">
+          <input message="Input"/>
+        </operation>
+      </portType>
+
+      <binding name="TestBinding" type="tns:TestPortType">
+        <soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http"/>
+        <operation name="TestOperation">
+          <soap:operation soapAction=""/>
+          <input>
+            <soap:header message="tns:InputHeader" part="header" use="literal" />
+            <soap:body use="literal"/>
+          </input>
+        </operation>
+      </binding>
+    </definitions>
+    """.strip())
+
+    root = wsdl.Document(wsdl_content, None)
+
+    binding = root.bindings['{http://tests.python-zeep.org/tns}TestBinding']
+    operation = binding.get('TestOperation')
+
+    assert operation.input.header.signature() == 'header: RequestHeader()'
+    assert operation.input.body.signature() == 'xsd:string'
+
+    header = root.types.get_element(
+        '{http://tests.python-zeep.org/tns}RequestHeader'
+    )('foo')
+    serialized = operation.input.serialize(
+        'ah1', _soapheaders={'header': header})
+    expected = """
+        <?xml version="1.0"?>
+        <soap-env:Envelope
+            xmlns:soap-env="http://schemas.xmlsoap.org/soap/envelope/">
+          <soap-env:Header>
+            <ns0:RequestHeader xmlns:ns0="http://tests.python-zeep.org/tns">foo</ns0:RequestHeader>
+          </soap-env:Header>
+          <soap-env:Body>
+            <ns0:Request xmlns:ns0="http://tests.python-zeep.org/tns">ah1</ns0:Request>
+          </soap-env:Body>
+        </soap-env:Envelope>
+    """
+    assert_nodes_equal(expected, serialized.content)
+
+
+def test_serialize():
+    wsdl_content = StringIO("""
+    <definitions xmlns="http://schemas.xmlsoap.org/wsdl/"
+                 xmlns:tns="http://tests.python-zeep.org/tns"
+                 xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
+                 xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+                 targetNamespace="http://tests.python-zeep.org/tns">
+      <types>
+        <xsd:schema targetNamespace="http://tests.python-zeep.org/tns"
+                    elementFormDefault="qualified">
+          <xsd:element name="Request">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="arg1" type="xsd:string"/>
+                <xsd:element name="arg2" type="xsd:string"/>
+              </xsd:sequence>
+            </xsd:complexType>
+          </xsd:element>
+        </xsd:schema>
+      </types>
+
+      <message name="Input">
+        <part element="tns:Request"/>
+      </message>
+
+      <portType name="TestPortType">
+        <operation name="TestOperation">
+          <input message="Input"/>
+        </operation>
+      </portType>
+
+      <binding name="TestBinding" type="tns:TestPortType">
+        <soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http"/>
+        <operation name="TestOperation">
+          <soap:operation soapAction=""/>
+          <input>
+            <soap:body use="literal"/>
+          </input>
+        </operation>
+      </binding>
+    </definitions>
+    """.strip())
+
+    root = wsdl.Document(wsdl_content, None)
+
+    binding = root.bindings['{http://tests.python-zeep.org/tns}TestBinding']
+    operation = binding.get('TestOperation')
+
+    serialized = operation.input.serialize(arg1='ah1', arg2='ah2')
+    expected = """
+        <?xml version="1.0"?>
+        <soap-env:Envelope
+            xmlns:soap-env="http://schemas.xmlsoap.org/soap/envelope/">
+          <soap-env:Body>
+            <ns0:Request xmlns:ns0="http://tests.python-zeep.org/tns">
+              <ns0:arg1>ah1</ns0:arg1>
+              <ns0:arg2>ah2</ns0:arg2>
+            </ns0:Request>
+          </soap-env:Body>
+        </soap-env:Envelope>
+    """
+    assert_nodes_equal(expected, serialized.content)
+
+
+def test_serialize_with_header():
+    wsdl_content = StringIO("""
+    <definitions xmlns="http://schemas.xmlsoap.org/wsdl/"
+                 xmlns:tns="http://tests.python-zeep.org/tns"
+                 xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
+                 xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+                 targetNamespace="http://tests.python-zeep.org/tns">
+      <types>
+        <xsd:schema targetNamespace="http://tests.python-zeep.org/tns"
+                    elementFormDefault="qualified">
+          <xsd:element name="Request">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="arg1" type="xsd:string"/>
+                <xsd:element name="arg2" type="xsd:string"/>
+              </xsd:sequence>
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="Authentication">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="username" type="xsd:string"/>
+              </xsd:sequence>
+            </xsd:complexType>
+          </xsd:element>
+        </xsd:schema>
+      </types>
+
+      <message name="Input">
+        <part element="tns:Request"/>
+        <part element="tns:Authentication" name="auth"/>
+      </message>
+
+      <portType name="TestPortType">
+        <operation name="TestOperation">
+          <input message="Input"/>
+        </operation>
+      </portType>
+
+      <binding name="TestBinding" type="tns:TestPortType">
+        <soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http"/>
+        <operation name="TestOperation">
+          <soap:operation soapAction=""/>
+          <input>
+            <soap:body use="literal"/>
+            <soap:header message="tns:Input" part="auth" use="literal"/>
+          </input>
+        </operation>
+      </binding>
+    </definitions>
+    """.strip())
+
+    root = wsdl.Document(wsdl_content, None)
+
+    binding = root.bindings['{http://tests.python-zeep.org/tns}TestBinding']
+    operation = binding.get('TestOperation')
+
+    AuthHeader = root.types.get_element('{http://tests.python-zeep.org/tns}Authentication')
+    auth_header = AuthHeader(username='mvantellingen')
+
+    serialized = operation.input.serialize(
+        arg1='ah1', arg2='ah2', _soapheaders=[auth_header])
+    serialized = operation.input.serialize(
+        arg1='ah1', arg2='ah2', _soapheaders=[auth_header])
+    expected = """
+        <?xml version="1.0"?>
+        <soap-env:Envelope
+            xmlns:soap-env="http://schemas.xmlsoap.org/soap/envelope/">
+          <soap-env:Header>
+            <ns0:Authentication xmlns:ns0="http://tests.python-zeep.org/tns">
+              <ns0:username>mvantellingen</ns0:username>
+            </ns0:Authentication>
+          </soap-env:Header>
+          <soap-env:Body>
+            <ns0:Request xmlns:ns0="http://tests.python-zeep.org/tns">
+              <ns0:arg1>ah1</ns0:arg1>
+              <ns0:arg2>ah2</ns0:arg2>
+            </ns0:Request>
+          </soap-env:Body>
+        </soap-env:Envelope>
+    """
+    assert_nodes_equal(expected, serialized.content)
+
+
+def test_serialize_with_headers_simple():
+    wsdl_content = StringIO("""
+    <definitions xmlns="http://schemas.xmlsoap.org/wsdl/"
+                 xmlns:tns="http://tests.python-zeep.org/tns"
+                 xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
+                 xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+                 targetNamespace="http://tests.python-zeep.org/tns">
+      <types>
+        <xsd:schema targetNamespace="http://tests.python-zeep.org/tns"
+                    elementFormDefault="qualified">
+          <xsd:element name="Request">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="arg1" type="xsd:string"/>
+                <xsd:element name="arg2" type="xsd:string"/>
+              </xsd:sequence>
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="Authentication">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="username" type="xsd:string"/>
+              </xsd:sequence>
+            </xsd:complexType>
+          </xsd:element>
+        </xsd:schema>
+      </types>
+
+      <message name="Input">
+        <part element="tns:Request"/>
+        <part element="tns:Authentication" name="Header"/>
+      </message>
+
+      <portType name="TestPortType">
+        <operation name="TestOperation">
+          <input message="Input"/>
+        </operation>
+      </portType>
+
+      <binding name="TestBinding" type="tns:TestPortType">
+        <soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http"/>
+        <operation name="TestOperation">
+          <soap:operation soapAction=""/>
+          <input>
+            <soap:body use="literal"/>
+            <soap:header message="tns:Input" part="Header" use="literal"/>
+          </input>
+        </operation>
+      </binding>
+    </definitions>
+    """.strip())
+
+    root = wsdl.Document(wsdl_content, None)
+
+    binding = root.bindings['{http://tests.python-zeep.org/tns}TestBinding']
+    operation = binding.get('TestOperation')
+
+    header = xsd.ComplexType(
+        xsd.Sequence([
+            xsd.Element('{http://www.w3.org/2005/08/addressing}Action', xsd.String()),
+            xsd.Element('{http://www.w3.org/2005/08/addressing}To', xsd.String()),
+        ])
+    )
+    header_value = header(Action='doehet', To='server')
+    serialized = operation.input.serialize(
+        arg1='ah1', arg2='ah2',
+        _soapheaders=[header_value])
+    expected = """
+        <?xml version="1.0"?>
+        <soap-env:Envelope
+            xmlns:soap-env="http://schemas.xmlsoap.org/soap/envelope/">
+          <soap-env:Header>
+            <ns0:Action xmlns:ns0="http://www.w3.org/2005/08/addressing">doehet</ns0:Action>
+            <ns1:To xmlns:ns1="http://www.w3.org/2005/08/addressing">server</ns1:To>
+          </soap-env:Header>
+          <soap-env:Body>
+            <ns0:Request xmlns:ns0="http://tests.python-zeep.org/tns">
+              <ns0:arg1>ah1</ns0:arg1>
+              <ns0:arg2>ah2</ns0:arg2>
+            </ns0:Request>
+          </soap-env:Body>
+        </soap-env:Envelope>
+    """
+    assert_nodes_equal(expected, serialized.content)
+
+
+def test_serialize_with_header_and_custom_mixed():
+    wsdl_content = StringIO("""
+    <definitions xmlns="http://schemas.xmlsoap.org/wsdl/"
+                 xmlns:tns="http://tests.python-zeep.org/tns"
+                 xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
+                 xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+                 targetNamespace="http://tests.python-zeep.org/tns">
+      <types>
+        <xsd:schema targetNamespace="http://tests.python-zeep.org/tns"
+                    elementFormDefault="qualified">
+          <xsd:element name="Request">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="arg1" type="xsd:string"/>
+                <xsd:element name="arg2" type="xsd:string"/>
+              </xsd:sequence>
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="Authentication">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="username" type="xsd:string"/>
+              </xsd:sequence>
+            </xsd:complexType>
+          </xsd:element>
+        </xsd:schema>
+      </types>
+
+      <message name="Input">
+        <part element="tns:Request"/>
+        <part element="tns:Authentication" name="Header"/>
+      </message>
+
+      <portType name="TestPortType">
+        <operation name="TestOperation">
+          <input message="Input"/>
+        </operation>
+      </portType>
+
+      <binding name="TestBinding" type="tns:TestPortType">
+        <soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http"/>
+        <operation name="TestOperation">
+          <soap:operation soapAction=""/>
+          <input>
+            <soap:body use="literal"/>
+            <soap:header message="tns:Input" part="Header" use="literal"/>
+          </input>
+        </operation>
+      </binding>
+    </definitions>
+    """.strip())
+
+    root = wsdl.Document(wsdl_content, None)
+
+    binding = root.bindings['{http://tests.python-zeep.org/tns}TestBinding']
+    operation = binding.get('TestOperation')
+
+    header = root.types.get_element(
+        '{http://tests.python-zeep.org/tns}Authentication'
+    )
+    header_1 = header(username='mvantellingen')
+
+    header = xsd.Element(
+        '{http://test.python-zeep.org/custom}custom',
+        xsd.ComplexType([
+            xsd.Element('{http://test.python-zeep.org/custom}foo', xsd.String()),
+        ])
+    )
+    header_2 = header(foo='bar')
+
+    serialized = operation.input.serialize(
+        arg1='ah1', arg2='ah2',
+        _soapheaders=[header_1, header_2])
+    expected = """
+        <?xml version="1.0"?>
+        <soap-env:Envelope
+            xmlns:soap-env="http://schemas.xmlsoap.org/soap/envelope/">
+          <soap-env:Header>
+            <ns0:Authentication xmlns:ns0="http://tests.python-zeep.org/tns">
+              <ns0:username>mvantellingen</ns0:username>
+            </ns0:Authentication>
+            <ns1:custom xmlns:ns1="http://test.python-zeep.org/custom">
+              <ns1:foo>bar</ns1:foo>
+            </ns1:custom>
+          </soap-env:Header>
+          <soap-env:Body>
+            <ns0:Request xmlns:ns0="http://tests.python-zeep.org/tns">
+              <ns0:arg1>ah1</ns0:arg1>
+              <ns0:arg2>ah2</ns0:arg2>
+            </ns0:Request>
+          </soap-env:Body>
+        </soap-env:Envelope>
+    """
+    assert_nodes_equal(expected, serialized.content)
+
+
+def test_serializer_with_header_custom_elm():
+    wsdl_content = StringIO("""
+    <definitions xmlns="http://schemas.xmlsoap.org/wsdl/"
+                 xmlns:tns="http://tests.python-zeep.org/tns"
+                 xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
+                 xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+                 targetNamespace="http://tests.python-zeep.org/tns">
+      <types>
+        <xsd:schema targetNamespace="http://tests.python-zeep.org/tns"
+                    elementFormDefault="qualified">
+          <xsd:element name="Request">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="arg1" type="xsd:string"/>
+                <xsd:element name="arg2" type="xsd:string"/>
+              </xsd:sequence>
+            </xsd:complexType>
+          </xsd:element>
+        </xsd:schema>
+      </types>
+
+      <message name="Input">
+        <part element="tns:Request"/>
+      </message>
+
+      <portType name="TestPortType">
+        <operation name="TestOperation">
+          <input message="Input"/>
+        </operation>
+      </portType>
+
+      <binding name="TestBinding" type="tns:TestPortType">
+        <soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http"/>
+        <operation name="TestOperation">
+          <soap:operation soapAction=""/>
+          <input>
+            <soap:body use="literal"/>
+          </input>
+        </operation>
+      </binding>
+    </definitions>
+    """.strip())
+
+    root = wsdl.Document(wsdl_content, None)
+
+    binding = root.bindings['{http://tests.python-zeep.org/tns}TestBinding']
+    operation = binding.get('TestOperation')
+
+    header = xsd.Element(
+        '{http://test.python-zeep.org/custom}auth',
+        xsd.ComplexType([
+            xsd.Element('{http://test.python-zeep.org/custom}username', xsd.String()),
+        ])
+    )
+
+    serialized = operation.input.serialize(
+        arg1='ah1', arg2='ah2', _soapheaders=[header(username='mvantellingen')])
+
+    expected = """
+        <?xml version="1.0"?>
+        <soap-env:Envelope
+            xmlns:soap-env="http://schemas.xmlsoap.org/soap/envelope/">
+          <soap-env:Header>
+            <ns0:auth xmlns:ns0="http://test.python-zeep.org/custom">
+              <ns0:username>mvantellingen</ns0:username>
+            </ns0:auth>
+          </soap-env:Header>
+          <soap-env:Body>
+            <ns0:Request xmlns:ns0="http://tests.python-zeep.org/tns">
+              <ns0:arg1>ah1</ns0:arg1>
+              <ns0:arg2>ah2</ns0:arg2>
+            </ns0:Request>
+          </soap-env:Body>
+        </soap-env:Envelope>
+    """
+    assert_nodes_equal(expected, serialized.content)
+
+
+def test_serializer_with_header_custom_xml():
+    wsdl_content = StringIO("""
+    <definitions xmlns="http://schemas.xmlsoap.org/wsdl/"
+                 xmlns:tns="http://tests.python-zeep.org/tns"
+                 xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
+                 xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+                 targetNamespace="http://tests.python-zeep.org/tns">
+      <types>
+        <xsd:schema targetNamespace="http://tests.python-zeep.org/tns"
+                    elementFormDefault="qualified">
+          <xsd:element name="Request">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="arg1" type="xsd:string"/>
+                <xsd:element name="arg2" type="xsd:string"/>
+              </xsd:sequence>
+            </xsd:complexType>
+          </xsd:element>
+        </xsd:schema>
+      </types>
+
+      <message name="Input">
+        <part element="tns:Request"/>
+      </message>
+
+      <portType name="TestPortType">
+        <operation name="TestOperation">
+          <input message="Input"/>
+        </operation>
+      </portType>
+
+      <binding name="TestBinding" type="tns:TestPortType">
+        <soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http"/>
+        <operation name="TestOperation">
+          <soap:operation soapAction=""/>
+          <input>
+            <soap:body use="literal"/>
+          </input>
+        </operation>
+      </binding>
+    </definitions>
+    """.strip())
+
+    root = wsdl.Document(wsdl_content, None)
+    binding = root.bindings['{http://tests.python-zeep.org/tns}TestBinding']
+    operation = binding.get('TestOperation')
+
+    header_value = etree.Element('{http://test.python-zeep.org/custom}auth')
+    etree.SubElement(
+        header_value, '{http://test.python-zeep.org/custom}username'
+    ).text = 'mvantellingen'
+
+    serialized = operation.input.serialize(
+        arg1='ah1', arg2='ah2', _soapheaders=[header_value])
+
+    expected = """
+        <?xml version="1.0"?>
+        <soap-env:Envelope
+            xmlns:soap-env="http://schemas.xmlsoap.org/soap/envelope/">
+          <soap-env:Header>
+            <ns0:auth xmlns:ns0="http://test.python-zeep.org/custom">
+              <ns0:username>mvantellingen</ns0:username>
+            </ns0:auth>
+          </soap-env:Header>
+          <soap-env:Body>
+            <ns0:Request xmlns:ns0="http://tests.python-zeep.org/tns">
+              <ns0:arg1>ah1</ns0:arg1>
+              <ns0:arg2>ah2</ns0:arg2>
+            </ns0:Request>
+          </soap-env:Body>
+        </soap-env:Envelope>
+    """
+    assert_nodes_equal(expected, serialized.content)
+
+
+def test_deserialize():
+    wsdl_content = StringIO("""
+    <definitions xmlns="http://schemas.xmlsoap.org/wsdl/"
+                 xmlns:tns="http://tests.python-zeep.org/tns"
+                 xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
+                 xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+                 targetNamespace="http://tests.python-zeep.org/tns">
+      <types>
+        <xsd:schema targetNamespace="http://tests.python-zeep.org/tns"
+                    elementFormDefault="qualified">
+          <xsd:element name="Request">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="arg1" type="xsd:string"/>
+                <xsd:element name="arg2" type="xsd:string"/>
+              </xsd:sequence>
+            </xsd:complexType>
+          </xsd:element>
+        </xsd:schema>
+      </types>
+
+      <message name="Input">
+        <part element="tns:Request"/>
+      </message>
+      <message name="Output">
+        <part element="tns:Request"/>
+      </message>
+
+      <portType name="TestPortType">
+        <operation name="TestOperation">
+          <input message="Input"/>
+          <output message="Output"/>
+        </operation>
+      </portType>
+
+      <binding name="TestBinding" type="tns:TestPortType">
+        <soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http"/>
+        <operation name="TestOperation">
+          <soap:operation soapAction=""/>
+          <input>
+            <soap:body use="literal"/>
+          </input>
+          <output>
+            <soap:body use="literal"/>
+          </output>
+        </operation>
+      </binding>
+    </definitions>
+    """.strip())
+
+    root = wsdl.Document(wsdl_content, None)
+
+    binding = root.bindings['{http://tests.python-zeep.org/tns}TestBinding']
+    operation = binding.get('TestOperation')
+
+    response_body = load_xml("""
+        <?xml version="1.0"?>
+        <soap-env:Envelope
+            xmlns:ns0="http://tests.python-zeep.org/tns"
+            xmlns:soap-env="http://schemas.xmlsoap.org/soap/envelope/">
+          <soap-env:Body>
+            <ns0:Request>
+              <ns0:arg1>ah1</ns0:arg1>
+              <ns0:arg2>ah2</ns0:arg2>
+            </ns0:Request>
+          </soap-env:Body>
+        </soap-env:Envelope>
+    """)
+    result = operation.process_reply(response_body)
+    assert result.arg1 == 'ah1'
+    assert result.arg2 == 'ah2'
+
+
+def test_deserialize_no_content():
+    wsdl_content = StringIO("""
+    <definitions xmlns="http://schemas.xmlsoap.org/wsdl/"
+                 xmlns:tns="http://tests.python-zeep.org/tns"
+                 xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
+                 xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+                 targetNamespace="http://tests.python-zeep.org/tns">
+      <types>
+        <xsd:schema targetNamespace="http://tests.python-zeep.org/tns"
+                    elementFormDefault="qualified">
+          <xsd:element name="Request" type="xsd:string"/>
+        </xsd:schema>
+      </types>
+
+      <message name="Input">
+        <part element="tns:Request"/>
+      </message>
+      <message name="Output">
+        <part element="tns:Request"/>
+      </message>
+
+      <portType name="TestPortType">
+        <operation name="TestOperation">
+          <input message="Input"/>
+          <output message="Output"/>
+        </operation>
+      </portType>
+
+      <binding name="TestBinding" type="tns:TestPortType">
+        <soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http"/>
+        <operation name="TestOperation">
+          <soap:operation soapAction=""/>
+          <input>
+            <soap:body use="literal"/>
+          </input>
+          <output>
+            <soap:body use="literal"/>
+          </output>
+        </operation>
+      </binding>
+    </definitions>
+    """.strip())
+
+    root = wsdl.Document(wsdl_content, None)
+
+    binding = root.bindings['{http://tests.python-zeep.org/tns}TestBinding']
+    operation = binding.get('TestOperation')
+
+    response_body = load_xml("""
+        <?xml version="1.0"?>
+        <soap-env:Envelope
+            xmlns:ns0="http://tests.python-zeep.org/tns"
+            xmlns:soap-env="http://schemas.xmlsoap.org/soap/envelope/">
+          <soap-env:Body>
+            <ns0:Request/>
+          </soap-env:Body>
+        </soap-env:Envelope>
+    """)
+    result = operation.process_reply(response_body)
+    assert result is None
+
+
+def test_deserialize_choice():
+    wsdl_content = StringIO("""
+    <definitions xmlns="http://schemas.xmlsoap.org/wsdl/"
+                 xmlns:tns="http://tests.python-zeep.org/tns"
+                 xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
+                 xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+                 targetNamespace="http://tests.python-zeep.org/tns">
+      <types>
+        <xsd:schema targetNamespace="http://tests.python-zeep.org/tns"
+                    elementFormDefault="qualified">
+          <xsd:element name="Request">
+            <xsd:complexType>
+              <xsd:choice>
+                <xsd:element name="arg1" type="xsd:string"/>
+                <xsd:element name="arg2" type="xsd:string"/>
+              </xsd:choice>
+            </xsd:complexType>
+          </xsd:element>
+        </xsd:schema>
+      </types>
+
+      <message name="Input">
+        <part element="tns:Request"/>
+      </message>
+      <message name="Output">
+        <part element="tns:Request"/>
+      </message>
+
+      <portType name="TestPortType">
+        <operation name="TestOperation">
+          <input message="Input"/>
+          <output message="Output"/>
+        </operation>
+      </portType>
+
+      <binding name="TestBinding" type="tns:TestPortType">
+        <soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http"/>
+        <operation name="TestOperation">
+          <soap:operation soapAction=""/>
+          <input>
+            <soap:body use="literal"/>
+          </input>
+          <output>
+            <soap:body use="literal"/>
+          </output>
+        </operation>
+      </binding>
+    </definitions>
+    """.strip())
+
+    root = wsdl.Document(wsdl_content, None)
+
+    binding = root.bindings['{http://tests.python-zeep.org/tns}TestBinding']
+    operation = binding.get('TestOperation')
+
+    response_body = load_xml("""
+        <?xml version="1.0"?>
+        <soap-env:Envelope
+            xmlns:ns0="http://tests.python-zeep.org/tns"
+            xmlns:soap-env="http://schemas.xmlsoap.org/soap/envelope/">
+          <soap-env:Body>
+            <ns0:Request>
+              <ns0:arg1>ah1</ns0:arg1>
+            </ns0:Request>
+          </soap-env:Body>
+        </soap-env:Envelope>
+    """)
+    result = operation.process_reply(response_body)
+    assert result.arg1 == 'ah1'
+
+
+def test_deserialize_one_part():
+    wsdl_content = StringIO("""
+    <definitions xmlns="http://schemas.xmlsoap.org/wsdl/"
+                 xmlns:tns="http://tests.python-zeep.org/tns"
+                 xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
+                 xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+                 targetNamespace="http://tests.python-zeep.org/tns">
+      <types>
+        <xsd:schema targetNamespace="http://tests.python-zeep.org/tns"
+                    elementFormDefault="qualified">
+          <xsd:element name="Request">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="arg1" type="xsd:string"/>
+                <xsd:element name="arg2" type="xsd:string"/>
+              </xsd:sequence>
+            </xsd:complexType>
+          </xsd:element>
+        </xsd:schema>
+      </types>
+
+      <message name="Input">
+        <part element="tns:Request"/>
+      </message>
+
+      <message name="Output">
+        <part element="tns:Request"/>
+      </message>
+
+      <portType name="TestPortType">
+        <operation name="TestOperation">
+          <input message="Input"/>
+          <output message="Output"/>
+        </operation>
+      </portType>
+
+      <binding name="TestBinding" type="tns:TestPortType">
+        <soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http"/>
+        <operation name="TestOperation">
+          <soap:operation soapAction=""/>
+          <input>
+            <soap:body use="literal"/>
+          </input>
+          <output>
+            <soap:body use="literal"/>
+          </output>
+        </operation>
+      </binding>
+    </definitions>
+    """.strip())
+
+    root = wsdl.Document(wsdl_content, None)
+
+    binding = root.bindings['{http://tests.python-zeep.org/tns}TestBinding']
+    operation = binding.get('TestOperation')
+
+    response_body = load_xml("""
+        <?xml version="1.0"?>
+        <soap-env:Envelope
+            xmlns:ns0="http://tests.python-zeep.org/tns"
+            xmlns:soap-env="http://schemas.xmlsoap.org/soap/envelope/">
+          <soap-env:Header>
+            <ns0:auth xmlns:ns0="http://test.python-zeep.org/custom">
+              <ns0:username>mvantellingen</ns0:username>
+            </ns0:auth>
+          </soap-env:Header>
+          <soap-env:Body>
+            <ns0:Request>
+              <ns0:arg1>ah1</ns0:arg1>
+              <ns0:arg2>ah2</ns0:arg2>
+            </ns0:Request>
+          </soap-env:Body>
+        </soap-env:Envelope>
+    """)  # noqa
+
+    serialized = operation.process_reply(response_body)
+    assert serialized.arg1 == 'ah1'
+    assert serialized.arg2 == 'ah2'
+
+
+def test_deserialize_with_headers():
+    wsdl_content = StringIO("""
+    <definitions xmlns="http://schemas.xmlsoap.org/wsdl/"
+                 xmlns:tns="http://tests.python-zeep.org/tns"
+                 xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
+                 xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+                 targetNamespace="http://tests.python-zeep.org/tns">
+      <types>
+        <xsd:schema targetNamespace="http://tests.python-zeep.org/tns"
+                    elementFormDefault="qualified">
+          <xsd:element name="Request1">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="arg1" type="xsd:string"/>
+              </xsd:sequence>
+            </xsd:complexType>
+          </xsd:element>
+
+          <xsd:element name="Request2">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="arg2" type="xsd:string"/>
+              </xsd:sequence>
+            </xsd:complexType>
+          </xsd:element>
+
+          <xsd:element name="Header1">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="username" type="xsd:string"/>
+              </xsd:sequence>
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="Header2" type="xsd:string"/>
+        </xsd:schema>
+      </types>
+
+      <message name="Input">
+        <part element="tns:Request1"/>
+      </message>
+
+      <message name="Output">
+        <part element="tns:Request1" name="request_1"/>
+        <part element="tns:Request2" name="request_2"/>
+        <part element="tns:Header1" name="header_1"/>
+        <part element="tns:Header2" name="header_2"/>
+      </message>
+
+      <portType name="TestPortType">
+        <operation name="TestOperation">
+          <input message="Input"/>
+          <output message="Output"/>
+        </operation>
+      </portType>
+
+      <binding name="TestBinding" type="tns:TestPortType">
+        <soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http"/>
+        <operation name="TestOperation">
+          <soap:operation soapAction=""/>
+          <input>
+            <soap:body use="literal"/>
+          </input>
+          <output>
+            <soap:body use="literal"/>
+            <soap:header message="tns:Output" part="header_1" use="literal">
+              <soap:headerfault message="tns:OutputFault"
+                    part="header_1_fault" use="literal"/>
+            </soap:header>
+            <soap:header message="tns:Output" part="header_2" use="literal"/>
+          </output>
+        </operation>
+      </binding>
+    </definitions>
+    """.strip())
+
+    root = wsdl.Document(wsdl_content, None)
+
+    binding = root.bindings['{http://tests.python-zeep.org/tns}TestBinding']
+    operation = binding.get('TestOperation')
+
+    response_body = load_xml("""
+        <?xml version="1.0"?>
+        <soap-env:Envelope
+            xmlns:ns0="http://tests.python-zeep.org/tns"
+            xmlns:soap-env="http://schemas.xmlsoap.org/soap/envelope/">
+          <soap-env:Header>
+            <ns0:Header1>
+              <ns0:username>mvantellingen</ns0:username>
+            </ns0:Header1>
+            <ns0:Header2>foo</ns0:Header2>
+          </soap-env:Header>
+          <soap-env:Body>
+            <ns0:Request1>
+              <ns0:arg1>ah1</ns0:arg1>
+            </ns0:Request1>
+            <ns0:Request2>
+              <ns0:arg2>ah2</ns0:arg2>
+            </ns0:Request2>
+          </soap-env:Body>
+        </soap-env:Envelope>
+    """)  # noqa
+
+    serialized = operation.process_reply(response_body)
+
+    assert operation.output.signature(as_output=True) == (
+        'body: {request_1: Request1(), request_2: Request2()}, header: {header_1: Header1(), header_2: Header2()}')  # noqa
+    assert serialized.body.request_1.arg1 == 'ah1'
+    assert serialized.body.request_2.arg2 == 'ah2'
+    assert serialized.header.header_1.username == 'mvantellingen'
+
+
+def test_serialize_any_type():
+    wsdl_content = StringIO("""
+    <definitions xmlns="http://schemas.xmlsoap.org/wsdl/"
+                 xmlns:tns="http://tests.python-zeep.org/tns"
+                 xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
+                 xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+                 targetNamespace="http://tests.python-zeep.org/tns">
+      <types>
+        <xsd:schema targetNamespace="http://tests.python-zeep.org/tns"
+                    elementFormDefault="qualified">
+          <xsd:element name="Request">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="arg1" type="xsd:anyType"/>
+              </xsd:sequence>
+            </xsd:complexType>
+          </xsd:element>
+        </xsd:schema>
+      </types>
+
+      <message name="Input">
+        <part element="tns:Request"/>
+      </message>
+
+      <portType name="TestPortType">
+        <operation name="TestOperation">
+          <input message="Input"/>
+        </operation>
+      </portType>
+
+      <binding name="TestBinding" type="tns:TestPortType">
+        <soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http"/>
+        <operation name="TestOperation">
+          <soap:operation soapAction=""/>
+          <input>
+            <soap:body use="literal"/>
+          </input>
+        </operation>
+      </binding>
+    </definitions>
+    """.strip())
+
+    root = wsdl.Document(wsdl_content, None)
+
+    binding = root.bindings['{http://tests.python-zeep.org/tns}TestBinding']
+    operation = binding.get('TestOperation')
+
+    serialized = operation.input.serialize(
+        arg1=xsd.AnyObject(xsd.String(), 'ah1'))
+    expected = """
+        <?xml version="1.0"?>
+        <soap-env:Envelope
+            xmlns:soap-env="http://schemas.xmlsoap.org/soap/envelope/">
+          <soap-env:Body>
+            <ns0:Request xmlns:ns0="http://tests.python-zeep.org/tns">
+              <ns0:arg1
+                xmlns:xs="http://www.w3.org/2001/XMLSchema"
+                xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+                xsi:type="xs:string">ah1</ns0:arg1>
+            </ns0:Request>
+          </soap-env:Body>
+        </soap-env:Envelope>
+    """
+    assert_nodes_equal(expected, serialized.content)
+    deserialized = operation.input.deserialize(serialized.content)
+
+    assert deserialized == 'ah1'
diff --git a/tests/test_wsdl_messages_http.py b/tests/test_wsdl_messages_http.py
new file mode 100644
index 0000000..aa40d25
--- /dev/null
+++ b/tests/test_wsdl_messages_http.py
@@ -0,0 +1,390 @@
+from six import StringIO
+
+from tests.utils import assert_nodes_equal, load_xml
+from zeep.wsdl import wsdl
+
+
+##
+# URLEncoded Message
+#
+def test_urlencoded_serialize():
+    wsdl_content = StringIO("""
+    <definitions xmlns="http://schemas.xmlsoap.org/wsdl/"
+                 xmlns:tns="http://tests.python-zeep.org/tns"
+                 xmlns:http="http://schemas.xmlsoap.org/wsdl/http/"
+                 xmlns:mime="http://schemas.xmlsoap.org/wsdl/mime/"
+                 xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
+                 xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+                 targetNamespace="http://tests.python-zeep.org/tns">
+
+      <message name="Input">
+        <part name="arg1" type="xsd:string"/>
+        <part name="arg2" type="xsd:string"/>
+      </message>
+      <message name="Output">
+        <part name="Body" type="xsd:string"/>
+      </message>
+
+      <portType name="TestPortType">
+        <operation name="TestOperation">
+          <input message="Input"/>
+          <output message="Output"/>
+        </operation>
+      </portType>
+
+      <binding name="TestBinding" type="tns:TestPortType">
+        <http:binding verb="POST"/>
+        <operation name="TestOperation">
+          <http:operation location="test-operation"/>
+          <input>
+            <http:urlEncoded/>
+          </input>
+          <output>
+            <mime:mimeXml part="Body"/>
+          </output>
+        </operation>
+      </binding>
+    </definitions>
+    """.strip())
+
+    root = wsdl.Document(wsdl_content, None)
+
+    binding = root.bindings['{http://tests.python-zeep.org/tns}TestBinding']
+    operation = binding.get('TestOperation')
+
+    assert operation.input.body.signature() == 'arg1: xsd:string, arg2: xsd:string'
+    assert operation.input.signature(as_output=False) == 'arg1: xsd:string, arg2: xsd:string'
+
+    assert operation.output.body.signature() == 'Body: xsd:string'
+    assert operation.output.signature(as_output=True) == 'xsd:string'
+
+    serialized = operation.input.serialize(arg1='ah1', arg2='ah2')
+    assert serialized.headers == {'Content-Type': 'text/xml; charset=utf-8'}
+    assert serialized.path == 'test-operation'
+    assert serialized.content == {'arg1': 'ah1', 'arg2': 'ah2'}
+
+
+##
+# URLReplacement Message
+#
+def test_urlreplacement_serialize():
+    wsdl_content = StringIO("""
+    <definitions xmlns="http://schemas.xmlsoap.org/wsdl/"
+                 xmlns:tns="http://tests.python-zeep.org/tns"
+                 xmlns:http="http://schemas.xmlsoap.org/wsdl/http/"
+                 xmlns:mime="http://schemas.xmlsoap.org/wsdl/mime/"
+                 xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
+                 xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+                 targetNamespace="http://tests.python-zeep.org/tns">
+
+      <message name="Input">
+        <part name="arg1" type="xsd:string"/>
+        <part name="arg2" type="xsd:string"/>
+      </message>
+      <message name="Output">
+        <part name="Body" type="xsd:string"/>
+      </message>
+
+      <portType name="TestPortType">
+        <operation name="TestOperation">
+          <input message="Input"/>
+          <output message="Output"/>
+        </operation>
+      </portType>
+
+      <binding name="TestBinding" type="tns:TestPortType">
+        <http:binding verb="POST"/>
+        <operation name="TestOperation">
+          <http:operation location="test-operation/(arg1)/(arg2)/"/>
+          <input>
+            <http:urlReplacement/>
+          </input>
+          <output>
+            <mime:mimeXml part="Body"/>
+          </output>
+        </operation>
+      </binding>
+    </definitions>
+    """.strip())
+
+    root = wsdl.Document(wsdl_content, None)
+
+    binding = root.bindings['{http://tests.python-zeep.org/tns}TestBinding']
+    operation = binding.get('TestOperation')
+
+    assert operation.input.body.signature() == 'arg1: xsd:string, arg2: xsd:string'
+    assert operation.input.signature(as_output=False) == 'arg1: xsd:string, arg2: xsd:string'
+
+    assert operation.output.body.signature() == 'Body: xsd:string'
+    assert operation.output.signature(as_output=True) == 'xsd:string'
+
+    serialized = operation.input.serialize(arg1='ah1', arg2='ah2')
+    assert serialized.headers == {'Content-Type': 'text/xml; charset=utf-8'}
+    assert serialized.path == 'test-operation/ah1/ah2/'
+    assert serialized.content == ''
+
+
+##
+# MimeContent Message
+#
+def test_mime_content_serialize_form_urlencoded():
+    wsdl_content = StringIO("""
+    <definitions xmlns="http://schemas.xmlsoap.org/wsdl/"
+                 xmlns:tns="http://tests.python-zeep.org/tns"
+                 xmlns:http="http://schemas.xmlsoap.org/wsdl/http/"
+                 xmlns:mime="http://schemas.xmlsoap.org/wsdl/mime/"
+                 xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
+                 xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+                 targetNamespace="http://tests.python-zeep.org/tns">
+
+      <message name="Input">
+        <part name="arg1" type="xsd:string"/>
+        <part name="arg2" type="xsd:string"/>
+      </message>
+      <message name="Output">
+        <part name="Body" type="xsd:string"/>
+      </message>
+
+      <portType name="TestPortType">
+        <operation name="TestOperation">
+          <input message="Input"/>
+          <output message="Output"/>
+        </operation>
+      </portType>
+
+      <binding name="TestBinding" type="tns:TestPortType">
+        <http:binding verb="POST"/>
+        <operation name="TestOperation">
+          <http:operation location="test-operation"/>
+          <input>
+            <mime:content type="application/x-www-form-urlencoded"/>
+          </input>
+          <output>
+            <mime:mimeXml part="Body"/>
+          </output>
+        </operation>
+      </binding>
+    </definitions>
+    """.strip())
+
+    root = wsdl.Document(wsdl_content, None)
+
+    binding = root.bindings['{http://tests.python-zeep.org/tns}TestBinding']
+    operation = binding.get('TestOperation')
+
+    assert operation.input.body.signature() == 'arg1: xsd:string, arg2: xsd:string'
+    assert operation.input.signature(as_output=False) == 'arg1: xsd:string, arg2: xsd:string'
+
+    assert operation.output.body.signature() == 'Body: xsd:string'
+    assert operation.output.signature(as_output=True) == 'xsd:string'
+
+    serialized = operation.input.serialize(arg1='ah1', arg2='ah2')
+    assert serialized.headers == {'Content-Type': 'application/x-www-form-urlencoded'}
+    assert serialized.path == 'test-operation'
+    assert serialized.content == 'arg1=ah1&arg2=ah2'
+
+
+def test_mime_content_serialize_text_xml():
+    wsdl_content = StringIO("""
+    <definitions xmlns="http://schemas.xmlsoap.org/wsdl/"
+                 xmlns:tns="http://tests.python-zeep.org/tns"
+                 xmlns:http="http://schemas.xmlsoap.org/wsdl/http/"
+                 xmlns:mime="http://schemas.xmlsoap.org/wsdl/mime/"
+                 xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
+                 xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+                 targetNamespace="http://tests.python-zeep.org/tns">
+
+      <message name="Input">
+        <part name="arg1" type="xsd:string"/>
+        <part name="arg2" type="xsd:string"/>
+      </message>
+      <message name="Output">
+        <part name="Body" type="xsd:string"/>
+      </message>
+
+      <portType name="TestPortType">
+        <operation name="TestOperation">
+          <input message="Input"/>
+          <output message="Output"/>
+        </operation>
+      </portType>
+
+      <binding name="TestBinding" type="tns:TestPortType">
+        <http:binding verb="POST"/>
+        <operation name="TestOperation">
+          <http:operation location="test-operation"/>
+          <input>
+            <mime:content type="text/xml"/>
+          </input>
+          <output>
+            <mime:mimeXml part="Body"/>
+          </output>
+        </operation>
+      </binding>
+    </definitions>
+    """.strip())
+
+    root = wsdl.Document(wsdl_content, None)
+
+    binding = root.bindings['{http://tests.python-zeep.org/tns}TestBinding']
+    operation = binding.get('TestOperation')
+
+    assert operation.input.body.signature() == 'arg1: xsd:string, arg2: xsd:string'
+    assert operation.input.signature(as_output=False) == 'arg1: xsd:string, arg2: xsd:string'
+
+    assert operation.output.body.signature() == 'Body: xsd:string'
+    assert operation.output.signature(as_output=True) == 'xsd:string'
+
+    serialized = operation.input.serialize(arg1='ah1', arg2='ah2')
+    assert serialized.headers == {'Content-Type': 'text/xml'}
+    assert serialized.path == 'test-operation'
+    assert_nodes_equal(
+        load_xml(serialized.content),
+        load_xml("<TestOperation><arg1>ah1</arg1><arg2>ah2</arg2></TestOperation>"))
+
+
+def test_mime_content_no_parts():
+    wsdl_content = StringIO("""
+    <definitions xmlns="http://schemas.xmlsoap.org/wsdl/"
+                 xmlns:tns="http://tests.python-zeep.org/tns"
+                 xmlns:http="http://schemas.xmlsoap.org/wsdl/http/"
+                 xmlns:mime="http://schemas.xmlsoap.org/wsdl/mime/"
+                 xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
+                 xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+                 targetNamespace="http://tests.python-zeep.org/tns">
+
+      <message name="Input"/>
+      <message name="Output">
+        <part name="Body" type="xsd:string"/>
+      </message>
+
+      <portType name="TestPortType">
+        <operation name="TestOperation">
+          <input message="Input"/>
+          <output message="Output"/>
+        </operation>
+      </portType>
+
+      <binding name="TestBinding" type="tns:TestPortType">
+        <http:binding verb="POST"/>
+        <operation name="TestOperation">
+          <http:operation location="/test-operation"/>
+          <input>
+            <mime:content type="application/x-www-form-urlencoded"/>
+          </input>
+          <output>
+            <mime:mimeXml part="Body"/>
+          </output>
+        </operation>
+      </binding>
+    </definitions>
+    """.strip())
+
+    root = wsdl.Document(wsdl_content, None)
+
+    binding = root.bindings['{http://tests.python-zeep.org/tns}TestBinding']
+    operation = binding.get('TestOperation')
+
+    assert operation.input.signature() == ''
+
+    serialized = operation.input.serialize()
+    assert serialized.content == ''
+
+
+def test_mime_xml_deserialize():
+    wsdl_content = StringIO("""
+    <definitions xmlns="http://schemas.xmlsoap.org/wsdl/"
+                 xmlns:tns="http://tests.python-zeep.org/tns"
+                 xmlns:http="http://schemas.xmlsoap.org/wsdl/http/"
+                 xmlns:mime="http://schemas.xmlsoap.org/wsdl/mime/"
+                 xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
+                 xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+                 targetNamespace="http://tests.python-zeep.org/tns">
+      <types>
+        <xsd:schema
+            xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+            xmlns:tns="http://tests.python-zeep.org/tns"
+            targetNamespace="http://tests.python-zeep.org/tns"
+                elementFormDefault="qualified">
+          <xsd:element name="response">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="item_1" type="xsd:string"/>
+                <xsd:element name="item_2" type="xsd:string"/>
+              </xsd:sequence>
+            </xsd:complexType>
+          </xsd:element>
+        </xsd:schema>
+      </types>
+
+      <message name="Input">
+        <part name="arg1" type="xsd:string"/>
+        <part name="arg2" type="xsd:string"/>
+      </message>
+      <message name="Output">
+        <part name="Body" element="tns:response"/>
+      </message>
+
+      <portType name="TestPortType">
+        <operation name="TestOperation">
+          <input message="Input"/>
+          <output message="Output"/>
+        </operation>
+      </portType>
+
+      <binding name="TestBinding" type="tns:TestPortType">
+        <http:binding verb="POST"/>
+        <operation name="TestOperation">
+          <http:operation location="/test-operation"/>
+          <input>
+            <mime:content type="application/x-www-form-urlencoded"/>
+          </input>
+          <output>
+            <mime:mimeXml part="Body"/>
+          </output>
+        </operation>
+      </binding>
+    </definitions>
+    """.strip())
+
+    root = wsdl.Document(wsdl_content, None)
+
+    binding = root.bindings['{http://tests.python-zeep.org/tns}TestBinding']
+    operation = binding.get('TestOperation')
+
+    assert operation.input.signature() == 'arg1: xsd:string, arg2: xsd:string'
+    assert operation.output.signature(as_output=True) == (
+        'item_1: xsd:string, item_2: xsd:string')
+
+    node = """
+        <response xmlns="http://tests.python-zeep.org/tns">
+          <item_1>foo</item_1>
+          <item_2>bar</item_2>
+        </response>
+    """.strip()
+
+    serialized = operation.output.deserialize(node)
+    assert serialized.item_1 == 'foo'
+    assert serialized.item_2 == 'bar'
+
+
+def test_mime_multipart_parse():
+    load_xml("""
+        <output
+            xmlns="http://schemas.xmlsoap.org/wsdl/"
+            xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
+            xmlns:mime="http://schemas.xmlsoap.org/wsdl/mime/">
+          <mime:multipartRelated>
+              <mime:part>
+                  <soap:body parts="body" use="literal"/>
+              </mime:part>
+              <mime:part>
+                  <mime:content part="test" type="text/html"/>
+              </mime:part>
+              <mime:part>
+                  <mime:content part="logo" type="image/gif"/>
+                  <mime:content part="logo" type="image/jpeg"/>
+              </mime:part>
+          </mime:multipartRelated>
+       </output>
+    """)
diff --git a/tests/test_wsdl_messages_rpc.py b/tests/test_wsdl_messages_rpc.py
new file mode 100644
index 0000000..123683d
--- /dev/null
+++ b/tests/test_wsdl_messages_rpc.py
@@ -0,0 +1,434 @@
+import io
+
+from six import StringIO
+
+from tests.utils import DummyTransport, assert_nodes_equal, load_xml
+from zeep.wsdl import wsdl
+
+
+def test_serialize():
+    wsdl_content = StringIO("""
+    <definitions xmlns="http://schemas.xmlsoap.org/wsdl/"
+                 xmlns:tns="http://tests.python-zeep.org/tns"
+                 xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
+                 xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+                 targetNamespace="http://tests.python-zeep.org/tns">
+
+      <message name="Input">
+        <part name="arg1" type="xsd:string"/>
+        <part name="arg2" type="xsd:string"/>
+      </message>
+
+      <portType name="TestPortType">
+        <operation name="TestOperation">
+          <input message="Input"/>
+        </operation>
+      </portType>
+
+      <binding name="TestBinding" type="tns:TestPortType">
+        <soap:binding style="rpc" transport="http://schemas.xmlsoap.org/soap/http"/>
+        <operation name="TestOperation">
+          <soap:operation soapAction=""/>
+          <input>
+            <soap:body use="encoded"
+                       namespace="http://test.python-zeep.org/tests/rpc"/>
+          </input>
+        </operation>
+      </binding>
+    </definitions>
+    """.strip())
+
+    root = wsdl.Document(wsdl_content, None)
+
+    binding = root.bindings['{http://tests.python-zeep.org/tns}TestBinding']
+    operation = binding.get('TestOperation')
+
+    assert operation.input.signature() == 'arg1: xsd:string, arg2: xsd:string'
+
+    serialized = operation.input.serialize(arg1='ah1', arg2='ah2')
+    expected = """
+        <?xml version="1.0"?>
+        <soap-env:Envelope
+            xmlns:soap-env="http://schemas.xmlsoap.org/soap/envelope/">
+          <soap-env:Body>
+            <ns0:TestOperation xmlns:ns0="http://test.python-zeep.org/tests/rpc">
+              <arg1>ah1</arg1>
+              <arg2>ah2</arg2>
+            </ns0:TestOperation>
+          </soap-env:Body>
+        </soap-env:Envelope>
+    """
+    assert_nodes_equal(expected, serialized.content)
+
+
+def test_serialize_empty_input():
+    wsdl_content = StringIO("""
+    <definitions xmlns="http://schemas.xmlsoap.org/wsdl/"
+                 xmlns:tns="http://tests.python-zeep.org/tns"
+                 xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
+                 xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+                 targetNamespace="http://tests.python-zeep.org/tns">
+
+      <portType name="TestPortType">
+        <operation name="TestOperation">
+          <input message="tns:TestOperationRequests"/>
+        </operation>
+      </portType>
+
+      <message name="TestOperationRequests"/>
+
+      <binding name="TestBinding" type="tns:TestPortType">
+        <soap:binding style="rpc" transport="http://schemas.xmlsoap.org/soap/http"/>
+        <operation name="TestOperation">
+          <soap:operation soapAction=""/>
+          <input>
+            <soap:body use="encoded"
+                       namespace="http://test.python-zeep.org/tests/rpc"/>
+          </input>
+        </operation>
+      </binding>
+    </definitions>
+    """.strip())
+
+    root = wsdl.Document(wsdl_content, None)
+
+    binding = root.bindings['{http://tests.python-zeep.org/tns}TestBinding']
+    operation = binding.get('TestOperation')
+
+    serialized = operation.input.serialize()
+    expected = """
+        <?xml version="1.0"?>
+        <soap-env:Envelope
+            xmlns:soap-env="http://schemas.xmlsoap.org/soap/envelope/">
+          <soap-env:Body>
+            <ns0:TestOperation xmlns:ns0="http://test.python-zeep.org/tests/rpc"/>
+          </soap-env:Body>
+        </soap-env:Envelope>
+    """
+    assert_nodes_equal(expected, serialized.content)
+
+
+def test_deserialize():
+    wsdl_content = StringIO("""
+    <definitions xmlns="http://schemas.xmlsoap.org/wsdl/"
+                 xmlns:tns="http://tests.python-zeep.org/tns"
+                 xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
+                 xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+                 targetNamespace="http://tests.python-zeep.org/tns">
+      <message name="Output">
+        <part name="result" type="xsd:string"/>
+      </message>
+
+      <portType name="TestPortType">
+        <operation name="TestOperation">
+          <output message="Output"/>
+        </operation>
+      </portType>
+
+      <binding name="TestBinding" type="tns:TestPortType">
+        <soap:binding style="rpc" transport="http://schemas.xmlsoap.org/soap/http"/>
+        <operation name="TestOperation">
+          <soap:operation soapAction=""/>
+          <output>
+            <soap:body use="encoded"
+                       namespace="http://test.python-zeep.org/tests/rpc"/>
+          </output>
+        </operation>
+      </binding>
+    </definitions>
+    """.strip())
+
+    root = wsdl.Document(wsdl_content, None)
+
+    binding = root.bindings['{http://tests.python-zeep.org/tns}TestBinding']
+    operation = binding.get('TestOperation')
+
+    document = load_xml("""
+        <soap-env:Envelope
+          xmlns:soap-env="http://schemas.xmlsoap.org/soap/envelope/">
+          <soap-env:Body>
+            <ns0:Output xmlns:ns0="http://test.python-zeep.org/tests/rpc">
+              <result>ah1</result>
+            </ns0:Output>
+          </soap-env:Body>
+        </soap-env:Envelope>
+    """)
+    assert operation.output.signature(True) == 'body: {result: xsd:string}, header: {}'
+    result = operation.output.deserialize(document)
+    assert result == 'ah1'
+
+
+def test_wsdl_array_of_simple_types():
+    wsdl_content = StringIO("""
+    <definitions xmlns="http://schemas.xmlsoap.org/wsdl/"
+        targetNamespace="http://tests.python-zeep.org/tns"
+        xmlns:tns="http://tests.python-zeep.org/tns"
+        xmlns:impl="http://tests.python-zeep.org/tns"
+        xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+        xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
+        xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
+        xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/">
+      <types>
+        <schema xmlns="http://www.w3.org/2001/XMLSchema" targetNamespace="http://tests.python-zeep.org/tns">
+          <complexType name="ArrayOfString">
+            <complexContent>
+              <restriction base="soapenc:Array">
+                <attribute ref="soapenc:arrayType" wsdl:arrayType="xsd:string[]"/>
+              </restriction>
+            </complexContent>
+          </complexType>
+        </schema>
+      </types>
+      <portType name="SimpleTypeArrayPortType">
+        <operation name="getSimpleArray">
+          <input message="tns:getSimpleArrayRequest"/>
+          <output message="tns:getSimpleArrayResponse"/>
+        </operation>
+      </portType>
+      <binding name="SimpleTypeArrayBinding" type="tns:SimpleTypeArrayPortType">
+        <soap:binding style="rpc" transport="http://schemas.xmlsoap.org/soap/http"/>
+        <operation name="getSimpleArray">
+          <soap:operation soapAction=""/>
+          <input>
+            <soap:body use="encoded" namespace="http://tests.python-zeep.org/tns" encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"/>
+          </input>
+          <output>
+            <soap:body parts="return" use="encoded" namespace="http://tests.python-zeep.org/tns" encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"/>
+          </output>
+        </operation>
+      </binding>
+      <message name="getSimpleArrayRequest"/>
+      <message name="getSimpleArrayResponse">
+        <part name="return" type="tns:ArrayOfString"/>
+      </message>
+    </definitions>
+    """.strip())  # noqa
+
+    transport = DummyTransport()
+    transport.bind(
+        'http://schemas.xmlsoap.org/soap/encoding/',
+        load_xml(io.open('tests/wsdl_files/soap-enc.xsd', 'r').read().encode('utf-8')))
+    root = wsdl.Document(wsdl_content, transport)
+
+    binding = root.bindings['{http://tests.python-zeep.org/tns}SimpleTypeArrayBinding']
+    operation = binding.get('getSimpleArray')
+
+    document = load_xml("""
+    <SOAP-ENV:Envelope SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
+        xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/"
+        xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
+        xmlns:ns1="http://tests.python-zeep.org/tns"
+        xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+      <SOAP-ENV:Body>
+        <ns1:getSimpleArrayResponse>
+            <return SOAP-ENC:arrayType="xsd:string[16]" xsi:type="ns1:ArrayOfString">
+                <item xsi:type="xsd:string">item</item>
+                <item xsi:type="xsd:string">and</item>
+                <item xsi:type="xsd:string">even</item>
+                <item xsi:type="xsd:string">more</item>
+                <item xsi:type="xsd:string">items</item>
+            </return>
+        </ns1:getSimpleArrayResponse>
+      </SOAP-ENV:Body>
+    </SOAP-ENV:Envelope>
+    """)
+
+    deserialized = operation.output.deserialize(document)
+    assert deserialized == ['item', 'and', 'even', 'more', 'items']
+
+
+def test_handle_incorrectly_qualified():
+    # Based on #176
+    wsdl_content = StringIO("""
+    <?xml version="1.0"?>
+    <wsdl:definitions
+        xmlns="http://schemas.xmlsoap.org/wsdl/"
+        xmlns:tns="http://tests.python-zeep.org/tns"
+        xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/"
+        xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
+        xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
+        xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+        targetNamespace="http://tests.python-zeep.org/tns">
+      <wsdl:message name="getResponse">
+        <wsdl:part name="getItemReturn" type="xsd:string"/>
+      </wsdl:message>
+      <wsdl:message name="getRequest"></wsdl:message>
+      <wsdl:portType name="Test">
+        <wsdl:operation name="getItem">
+          <wsdl:input message="tns:getRequest" name="getRequest"/>
+          <wsdl:output message="tns:getResponse" name="getResponse"/>
+        </wsdl:operation>
+      </wsdl:portType>
+      <wsdl:binding name="TestSoapBinding" type="tns:Test">
+        <soap:binding style="rpc" transport="http://schemas.xmlsoap.org/soap/http"/>
+        <wsdl:operation name="getItem">
+          <soap:operation soapAction=""/>
+          <wsdl:input name="getRequest">
+            <soap:body
+                encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
+                namespace="http://tests.python-zeep.org/tns" use="encoded"/>
+          </wsdl:input>
+          <wsdl:output name="getResponse">
+            <soap:body
+                encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
+                namespace="http://tests.python-zeep.org/tns" use="encoded"/>
+          </wsdl:output>
+        </wsdl:operation>
+      </wsdl:binding>
+      <wsdl:service name="TestService">
+        <wsdl:port binding="tns:TestSoapBinding" name="Test">
+          <soap:address location="http://test.python-zeeo.org/rpc"/>
+        </wsdl:port>
+      </wsdl:service>
+    </wsdl:definitions>
+    """.strip())
+
+    transport = DummyTransport()
+    root = wsdl.Document(wsdl_content, transport)
+
+    binding = root.bindings['{http://tests.python-zeep.org/tns}TestSoapBinding']
+    operation = binding.get('getItem')
+
+    document = load_xml("""
+    <soapenv:Envelope
+        xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
+        xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+      <soapenv:Body>
+        <ns1:getResponse
+            soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
+            xmlns:ns1="http://tests.python-zeep.org/tns">
+          <ns1:getItemReturn xsi:type="xsd:string">foobar</ns1:getItemReturn>
+        </ns1:getResponse>
+      </soapenv:Body>
+    </soapenv:Envelope>
+    """)
+    deserialized = operation.output.deserialize(document)
+    assert deserialized == 'foobar'
+
+
+def test_deserialize_rpc_literal():
+    # Based on #219
+    wsdl_content = StringIO("""
+    <?xml version="1.0"?>
+    <wsdl:definitions
+        xmlns="http://schemas.xmlsoap.org/wsdl/"
+        xmlns:tns="http://tests.python-zeep.org/tns"
+        xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/"
+        xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
+        xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
+        xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+        targetNamespace="http://tests.python-zeep.org/tns">
+
+      <wsdl:message name="getItemSoapIn"></wsdl:message>
+      <wsdl:message name="getItemSoapOut">
+        <wsdl:part name="getItemReturn" type="xsd:string"/>
+      </wsdl:message>
+
+      <wsdl:portType name="Test">
+        <wsdl:operation name="getItem">
+          <wsdl:input message="tns:getItemSoapIn"/>
+          <wsdl:output message="tns:getItemSoapOut"/>
+        </wsdl:operation>
+      </wsdl:portType>
+
+      <wsdl:binding name="TestSoapBinding" type="tns:Test">
+        <soap:binding style="rpc" transport="http://schemas.xmlsoap.org/soap/http"/>
+        <wsdl:operation name="getItem">
+          <soap:operation soapAction=""/>
+          <wsdl:input>
+            <soap:body
+                encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
+                namespace="http://tests.python-zeep.org/tns" use="encoded"/>
+          </wsdl:input>
+          <wsdl:output>
+            <soap:body
+                encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
+                namespace="http://tests.python-zeep.org/tns" use="encoded"/>
+          </wsdl:output>
+        </wsdl:operation>
+      </wsdl:binding>
+      <wsdl:service name="TestService">
+        <wsdl:port binding="tns:TestSoapBinding" name="Test">
+          <soap:address location="http://test.python-zeeo.org/rpc"/>
+        </wsdl:port>
+      </wsdl:service>
+    </wsdl:definitions>
+    """.strip())
+
+    transport = DummyTransport()
+    root = wsdl.Document(wsdl_content, transport)
+
+    binding = root.bindings['{http://tests.python-zeep.org/tns}TestSoapBinding']
+    operation = binding.get('getItem')
+
+    document = load_xml("""
+    <soapenv:Envelope
+        xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
+        xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+      <soapenv:Body>
+        <ns1:getItemResponse
+            soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
+            xmlns:ns1="http://tests.python-zeep.org/tns">
+          <ns1:getItemReturn xsi:type="xsd:string">foobar</ns1:getItemReturn>
+        </ns1:getItemResponse>
+      </soapenv:Body>
+    </soapenv:Envelope>
+    """)
+    deserialized = operation.output.deserialize(document)
+    assert deserialized == 'foobar'
+
+
+def test_deserialize():
+    wsdl_content = StringIO("""
+    <definitions xmlns="http://schemas.xmlsoap.org/wsdl/"
+                 xmlns:tns="http://tests.python-zeep.org/tns"
+                 xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
+                 xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+                 xmlns:wsam="http://www.w3.org/2007/05/addressing/metadata"
+                 targetNamespace="http://tests.python-zeep.org/tns">
+
+
+      <message name="clearFoo">
+        <part name="code" type="xsd:string"/>
+      </message>
+      <message name="clearFooResponse"/>
+
+      <portType name="TestPortType">
+        <operation name="clearFoo">
+          <input wsam:Action="http://foo.services.example.com/Util/clearFooRequest" message="tns:clearFoo"/>
+          <output wsam:Action="http://foo.services.example.com/Util/clearFooResponse" message="tns:clearFooResponse"/>
+        </operation>
+      </portType>
+
+      <binding name="TestBinding" type="tns:TestPortType">
+        <soap:binding style="rpc" transport="http://schemas.xmlsoap.org/soap/http"/>
+        <operation name="clearFoo">
+          <soap:operation soapAction=""/>
+          <input>
+            <soap:body use="literal" namespace="http://foo.services.example.com/"/>
+          </input>
+          <output>
+            <soap:body use="literal" namespace="http://foo.services.example.com/"/>
+          </output>
+        </operation>
+      </binding>
+    </definitions>
+    """.strip())
+
+    root = wsdl.Document(wsdl_content, None)
+    binding = root.bindings['{http://tests.python-zeep.org/tns}TestBinding']
+    operation = binding.get('clearFoo')
+
+    document = load_xml("""
+        <?xml version="1.0" ?>
+        <S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/">
+            <S:Body>
+                <ns2:clearFooResponse xmlns:ns2="http://foo.services.example.com/"/>
+            </S:Body>
+        </S:Envelope>
+    """)
+    result = operation.output.deserialize(document)
+    assert result is None
diff --git a/tests/test_wsdl_soap.py b/tests/test_wsdl_soap.py
new file mode 100644
index 0000000..04a9fc9
--- /dev/null
+++ b/tests/test_wsdl_soap.py
@@ -0,0 +1,110 @@
+from lxml import etree
+
+from tests.utils import load_xml
+from zeep.exceptions import Fault
+from zeep.wsdl import bindings
+
+
+def test_soap11_process_error():
+    response = load_xml("""
+        <soapenv:Envelope
+            xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
+            xmlns:stoc="http://example.com/stockquote.xsd">
+          <soapenv:Body>
+            <soapenv:Fault>
+              <faultcode>fault-code</faultcode>
+              <faultstring>fault-string</faultstring>
+              <detail>
+                <e:myFaultDetails xmlns:e="http://myexample.org/faults">
+                  <e:message>detail-message</e:message>
+                  <e:errorcode>detail-code</e:errorcode>
+                </e:myFaultDetails>
+              </detail>
+            </soapenv:Fault>
+          </soapenv:Body>
+        </soapenv:Envelope>
+    """)
+    binding = bindings.Soap11Binding(
+        wsdl=None, name=None, port_name=None, transport=None,
+        default_style=None)
+
+    try:
+        binding.process_error(response, None)
+        assert False
+    except Fault as exc:
+        assert exc.message == 'fault-string'
+        assert exc.code == 'fault-code'
+        assert exc.actor is None
+        assert exc.subcodes is None
+        assert 'detail-message' in etree.tostring(exc.detail).decode('utf-8')
+
+
+def test_soap12_process_error():
+    response = """
+        <soapenv:Envelope
+            xmlns="http://example.com/example1"
+            xmlns:ex="http://example.com/example2"
+            xmlns:soapenv="http://www.w3.org/2003/05/soap-envelope">
+          <soapenv:Body>
+            <soapenv:Fault>
+             <soapenv:Code>
+               <soapenv:Value>fault-code</soapenv:Value>
+               %s
+             </soapenv:Code>
+             <soapenv:Reason>
+              <soapenv:Text xml:lang="en-US">us-error</soapenv:Text>
+              <soapenv:Text xml:lang="nl-NL">nl-error</soapenv:Text>
+             </soapenv:Reason>
+             <soapenv:Detail>
+              <e:myFaultDetails
+                xmlns:e="http://myexample.org/faults" >
+                <e:message>Invalid credit card details</e:message>
+                <e:errorcode>999</e:errorcode>
+              </e:myFaultDetails>
+             </soapenv:Detail>
+           </soapenv:Fault>
+         </soapenv:Body>
+        </soapenv:Envelope>
+    """
+    subcode = """
+               <soapenv:Subcode>
+                 <soapenv:Value>%s</soapenv:Value>
+                 %s
+               </soapenv:Subcode>
+    """
+    binding = bindings.Soap12Binding(
+        wsdl=None, name=None, port_name=None, transport=None,
+        default_style=None)
+
+    try:
+        binding.process_error(load_xml(response % ""), None)
+        assert False
+    except Fault as exc:
+        assert exc.message == 'us-error'
+        assert exc.code == 'fault-code'
+        assert exc.subcodes == []
+
+    try:
+        binding.process_error(
+            load_xml(response % subcode % ("fault-subcode1", "")), None)
+        assert False
+    except Fault as exc:
+        assert exc.message == 'us-error'
+        assert exc.code == 'fault-code'
+        assert len(exc.subcodes) == 1
+        assert exc.subcodes[0].namespace == 'http://example.com/example1'
+        assert exc.subcodes[0].localname == 'fault-subcode1'
+
+    try:
+        binding.process_error(
+            load_xml(response % subcode % ("fault-subcode1", subcode % ("ex:fault-subcode2", ""))),
+            None)
+        assert False
+    except Fault as exc:
+        assert exc.message == 'us-error'
+        assert exc.code == 'fault-code'
+        assert len(exc.subcodes) == 2
+        assert exc.subcodes[0].namespace == 'http://example.com/example1'
+        assert exc.subcodes[0].localname == 'fault-subcode1'
+        assert exc.subcodes[1].namespace == 'http://example.com/example2'
+        assert exc.subcodes[1].localname == 'fault-subcode2'
diff --git a/tests/test_wsse_username.py b/tests/test_wsse_username.py
new file mode 100644
index 0000000..690b818
--- /dev/null
+++ b/tests/test_wsse_username.py
@@ -0,0 +1,238 @@
+import datetime
+import os
+
+import pytest
+import requests_mock
+from freezegun import freeze_time
+
+from tests.utils import assert_nodes_equal, load_xml
+from zeep import client
+from zeep.wsse.username import UsernameToken
+
+
+ at pytest.mark.requests
+def test_integration():
+    client_obj = client.Client(
+        'tests/wsdl_files/soap.wsdl',
+        wsse=UsernameToken('username', 'password'))
+
+    response = """
+    <?xml version="1.0"?>
+    <soapenv:Envelope
+        xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
+        xmlns:stoc="http://example.com/stockquote.xsd">
+       <soapenv:Header/>
+       <soapenv:Body>
+          <stoc:TradePrice>
+             <price>120.123</price>
+          </stoc:TradePrice>
+       </soapenv:Body>
+    </soapenv:Envelope>
+    """.strip()
+
+    with requests_mock.mock() as m:
+        m.post('http://example.com/stockquote', text=response)
+        result = client_obj.service.GetLastTradePrice('foobar')
+        assert result == 120.123
+
+
+def test_password_text():
+    envelope = load_xml("""
+        <soap-env:Envelope
+            xmlns:ns0="http://example.com/stockquote.xsd"
+            xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
+            xmlns:soap-env="http://schemas.xmlsoap.org/soap/envelope/"
+            xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
+            xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+        >
+          <soap-env:Body>
+            <ns0:TradePriceRequest>
+              <tickerSymbol>foobar</tickerSymbol>
+              <ns0:country/>
+            </ns0:TradePriceRequest>
+          </soap-env:Body>
+        </soap-env:Envelope>
+    """)
+
+    token = UsernameToken('michael', 'geheim')
+    envelope, headers = token.sign(envelope, {})
+    expected = """
+        <soap-env:Envelope
+            xmlns:ns0="http://example.com/stockquote.xsd"
+            xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
+            xmlns:soap-env="http://schemas.xmlsoap.org/soap/envelope/"
+            xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
+            xmlns:xsd="http://www.w3.org/2001/XMLSchema">
+          <soap-env:Header>
+            <ns0:Security xmlns:ns0="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
+              <ns0:UsernameToken>
+                <ns0:Username>michael</ns0:Username>
+                <ns0:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText">geheim</ns0:Password>
+              </ns0:UsernameToken>
+            </ns0:Security>
+          </soap-env:Header>
+          <soap-env:Body>
+            <ns0:TradePriceRequest>
+              <tickerSymbol>foobar</tickerSymbol>
+              <ns0:country/>
+            </ns0:TradePriceRequest>
+          </soap-env:Body>
+        </soap-env:Envelope>
+    """  # noqa
+    assert_nodes_equal(envelope, expected)
+
+
+ at freeze_time('2016-05-08 12:00:00')
+def test_password_digest(monkeypatch):
+    monkeypatch.setattr(os, 'urandom', lambda x: b'mocked-random')
+
+    envelope = load_xml("""
+        <soap-env:Envelope
+            xmlns:ns0="http://example.com/stockquote.xsd"
+            xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
+            xmlns:soap-env="http://schemas.xmlsoap.org/soap/envelope/"
+            xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
+            xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+        >
+          <soap-env:Body>
+            <ns0:TradePriceRequest>
+              <tickerSymbol>foobar</tickerSymbol>
+              <ns0:country/>
+            </ns0:TradePriceRequest>
+          </soap-env:Body>
+        </soap-env:Envelope>
+    """)
+
+    token = UsernameToken('michael', 'geheim', use_digest=True)
+    envelope, headers = token.sign(envelope, {})
+    expected = """
+        <soap-env:Envelope
+            xmlns:ns0="http://example.com/stockquote.xsd"
+            xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
+            xmlns:soap-env="http://schemas.xmlsoap.org/soap/envelope/"
+            xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
+            xmlns:xsd="http://www.w3.org/2001/XMLSchema">
+          <soap-env:Header>
+            <ns0:Security xmlns:ns0="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
+              <ns0:UsernameToken>
+                <ns0:Username>michael</ns0:Username>
+                <ns0:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordDigest">hVicspAQSg70JNhe67OHqD9gexc=</ns0:Password>
+                <ns0:Nonce EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary">bW9ja2VkLXJhbmRvbQ==</ns0:Nonce>
+                <ns0:Created xmlns:ns0="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">2016-05-08T12:00:00+00:00</ns0:Created>
+              </ns0:UsernameToken>
+            </ns0:Security>
+          </soap-env:Header>
+          <soap-env:Body>
+            <ns0:TradePriceRequest>
+              <tickerSymbol>foobar</tickerSymbol>
+              <ns0:country/>
+            </ns0:TradePriceRequest>
+          </soap-env:Body>
+        </soap-env:Envelope>
+    """  # noqa
+    assert_nodes_equal(envelope, expected)
+
+
+ at freeze_time('2016-05-08 12:00:00')
+def test_password_digest_custom(monkeypatch):
+    monkeypatch.setattr(os, 'urandom', lambda x: b'mocked-random')
+
+    envelope = load_xml("""
+        <soap-env:Envelope
+            xmlns:ns0="http://example.com/stockquote.xsd"
+            xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
+            xmlns:soap-env="http://schemas.xmlsoap.org/soap/envelope/"
+            xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
+            xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+        >
+          <soap-env:Body>
+            <ns0:TradePriceRequest>
+              <tickerSymbol>foobar</tickerSymbol>
+              <ns0:country/>
+            </ns0:TradePriceRequest>
+          </soap-env:Body>
+        </soap-env:Envelope>
+    """)
+
+    created = datetime.datetime(2016, 6, 4, 20, 10)
+    token = UsernameToken(
+        'michael', password_digest='12345', use_digest=True,
+        nonce='iets', created=created)
+    envelope, headers = token.sign(envelope, {})
+    expected = """
+        <soap-env:Envelope
+            xmlns:ns0="http://example.com/stockquote.xsd"
+            xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
+            xmlns:soap-env="http://schemas.xmlsoap.org/soap/envelope/"
+            xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
+            xmlns:xsd="http://www.w3.org/2001/XMLSchema">
+          <soap-env:Header>
+            <ns0:Security xmlns:ns0="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
+              <ns0:UsernameToken>
+                <ns0:Username>michael</ns0:Username>
+                <ns0:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordDigest">12345</ns0:Password>
+                <ns0:Nonce EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary">aWV0cw==</ns0:Nonce>
+                <ns0:Created xmlns:ns0="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">2016-06-04T20:10:00+00:00</ns0:Created>
+              </ns0:UsernameToken>
+            </ns0:Security>
+          </soap-env:Header>
+          <soap-env:Body>
+            <ns0:TradePriceRequest>
+              <tickerSymbol>foobar</tickerSymbol>
+              <ns0:country/>
+            </ns0:TradePriceRequest>
+          </soap-env:Body>
+        </soap-env:Envelope>
+    """  # noqa
+    assert_nodes_equal(envelope, expected)
+
+
+def test_password_prepared():
+    envelope = load_xml("""
+        <soap-env:Envelope
+            xmlns:ns0="http://example.com/stockquote.xsd"
+            xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
+            xmlns:soap-env="http://schemas.xmlsoap.org/soap/envelope/"
+            xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
+            xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+        >
+          <soap-env:Header xmlns:ns0="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
+            <ns0:Security>
+              <ns0:UsernameToken/>
+            </ns0:Security>
+          </soap-env:Header>
+          <soap-env:Body>
+            <ns0:TradePriceRequest>
+              <tickerSymbol>foobar</tickerSymbol>
+              <ns0:country/>
+            </ns0:TradePriceRequest>
+          </soap-env:Body>
+        </soap-env:Envelope>
+    """)  # noqa
+
+    token = UsernameToken('michael', 'geheim')
+    envelope, headers = token.sign(envelope, {})
+    expected = """
+        <soap-env:Envelope
+            xmlns:ns0="http://example.com/stockquote.xsd"
+            xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
+            xmlns:soap-env="http://schemas.xmlsoap.org/soap/envelope/"
+            xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
+            xmlns:xsd="http://www.w3.org/2001/XMLSchema">
+          <soap-env:Header xmlns:ns0="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
+            <ns0:Security>
+              <ns0:UsernameToken>
+                <ns0:Username>michael</ns0:Username>
+                <ns0:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText">geheim</ns0:Password>
+              </ns0:UsernameToken>
+            </ns0:Security>
+          </soap-env:Header>
+          <soap-env:Body>
+            <ns0:TradePriceRequest>
+              <tickerSymbol>foobar</tickerSymbol>
+              <ns0:country/>
+            </ns0:TradePriceRequest>
+          </soap-env:Body>
+        </soap-env:Envelope>
+    """  # noqa
+    assert_nodes_equal(envelope, expected)
diff --git a/tests/test_wsse_utils.py b/tests/test_wsse_utils.py
new file mode 100644
index 0000000..8b3a3aa
--- /dev/null
+++ b/tests/test_wsse_utils.py
@@ -0,0 +1,25 @@
+from lxml import etree
+
+from zeep.wsse import utils
+
+
+def test_get_security_header():
+    doc = etree.fromstring("""
+        <soap-env:Envelope
+            xmlns:ns0="http://example.com/stockquote.xsd"
+            xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
+            xmlns:soap-env="http://schemas.xmlsoap.org/soap/envelope/"
+            xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
+            xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+        >
+          <soap-env:Body>
+            <ns0:TradePriceRequest>
+              <tickerSymbol>foobar</tickerSymbol>
+              <ns0:country/>
+            </ns0:TradePriceRequest>
+          </soap-env:Body>
+        </soap-env:Envelope>
+    """.strip())
+
+    element = utils.get_security_header(doc)
+    assert element.tag == '{http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd}Security'  # noqa
diff --git a/tests/test_xsd_any.py b/tests/test_xsd_any.py
new file mode 100644
index 0000000..4137b34
--- /dev/null
+++ b/tests/test_xsd_any.py
@@ -0,0 +1,280 @@
+import datetime
+
+import pytest
+from lxml import etree
+
+from tests.utils import assert_nodes_equal, load_xml
+from zeep import xsd
+
+
+def get_any_schema():
+    return xsd.Schema(load_xml("""
+        <?xml version="1.0"?>
+        <schema xmlns="http://www.w3.org/2001/XMLSchema"
+                xmlns:tns="http://tests.python-zeep.org/"
+                targetNamespace="http://tests.python-zeep.org/"
+                elementFormDefault="qualified">
+          <element name="item" type="string"/>
+          <element name="container">
+            <complexType>
+              <sequence>
+                <any/>
+              </sequence>
+            </complexType>
+          </element>
+        </schema>
+    """))
+
+
+def test_any_simple():
+    schema = get_any_schema()
+
+    item_elm = schema.get_element('{http://tests.python-zeep.org/}item')
+    assert isinstance(item_elm.type, xsd.String)
+
+    container_elm = schema.get_element('{http://tests.python-zeep.org/}container')
+
+    # Create via arg
+    obj = container_elm(xsd.AnyObject(item_elm, item_elm('argh')))
+    node = etree.Element('document')
+    container_elm.render(node, obj)
+    expected = """
+        <document>
+            <ns0:container xmlns:ns0="http://tests.python-zeep.org/">
+                <ns0:item>argh</ns0:item>
+            </ns0:container>
+        </document>
+    """
+    assert_nodes_equal(expected, node)
+    item = container_elm.parse(node.getchildren()[0], schema)
+    assert item._value_1 == 'argh'
+
+    # Create via kwarg _value_1
+    obj = container_elm(_value_1=xsd.AnyObject(item_elm, item_elm('argh')))
+    node = etree.Element('document')
+    container_elm.render(node, obj)
+    expected = """
+        <document>
+            <ns0:container xmlns:ns0="http://tests.python-zeep.org/">
+                <ns0:item>argh</ns0:item>
+            </ns0:container>
+        </document>
+    """
+    assert_nodes_equal(expected, node)
+    item = container_elm.parse(node.getchildren()[0], schema)
+    assert item._value_1 == 'argh'
+
+
+def test_any_value_element_tree():
+    schema = get_any_schema()
+
+    item = etree.Element('{http://tests.python-zeep.org}lxml')
+    etree.SubElement(item, 'node').text = 'foo'
+
+    container_elm = schema.get_element('{http://tests.python-zeep.org/}container')
+
+    # Create via arg
+    obj = container_elm(item)
+    node = etree.Element('document')
+    container_elm.render(node, obj)
+    expected = """
+        <document>
+            <ns0:container xmlns:ns0="http://tests.python-zeep.org/">
+                <ns0:lxml xmlns:ns0="http://tests.python-zeep.org">
+                    <node>foo</node>
+                </ns0:lxml>
+            </ns0:container>
+        </document>
+    """
+    assert_nodes_equal(expected, node)
+    item = container_elm.parse(node.getchildren()[0], schema)
+    assert isinstance(item._value_1, etree._Element)
+    assert item._value_1.tag == '{http://tests.python-zeep.org}lxml'
+
+
+def test_any_value_invalid():
+    schema = get_any_schema()
+
+    class SomeThing(object):
+        pass
+
+    container_elm = schema.get_element('{http://tests.python-zeep.org/}container')
+
+    # Create via arg
+    item = SomeThing()
+    obj = container_elm(item)
+    node = etree.Element('document')
+
+    with pytest.raises(TypeError):
+        container_elm.render(node, obj)
+
+
+def test_any_with_ref():
+    schema = xsd.Schema(load_xml("""
+        <?xml version="1.0"?>
+        <schema xmlns="http://www.w3.org/2001/XMLSchema"
+                xmlns:tns="http://tests.python-zeep.org/"
+                targetNamespace="http://tests.python-zeep.org/"
+                   elementFormDefault="qualified">
+          <element name="item" type="string"/>
+          <element name="container">
+            <complexType>
+              <sequence>
+                <element ref="tns:item"/>
+                <any/>
+                <any maxOccurs="unbounded"/>
+              </sequence>
+            </complexType>
+          </element>
+        </schema>
+    """))
+
+    item_elm = schema.get_element('{http://tests.python-zeep.org/}item')
+    assert isinstance(item_elm.type, xsd.String)
+
+    container_elm = schema.get_element('{http://tests.python-zeep.org/}container')
+    obj = container_elm(
+        item='bar',
+        _value_1=xsd.AnyObject(item_elm, item_elm('argh')))
+
+    node = etree.Element('document')
+    container_elm.render(node, obj)
+    expected = """
+        <document>
+            <ns0:container xmlns:ns0="http://tests.python-zeep.org/">
+                <ns0:item>bar</ns0:item>
+                <ns0:item>argh</ns0:item>
+            </ns0:container>
+        </document>
+    """
+    assert_nodes_equal(expected, node)
+    item = container_elm.parse(node.getchildren()[0], schema)
+    assert item.item == 'bar'
+    assert item._value_1 == 'argh'
+
+
+def test_element_any_parse():
+    node = load_xml("""
+        <xsd:schema
+            elementFormDefault="qualified"
+            targetNamespace="https://tests.python-zeep.org"
+            xmlns:xsd="http://www.w3.org/2001/XMLSchema">
+          <xsd:element name="container">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:any/>
+              </xsd:sequence>
+            </xsd:complexType>
+          </xsd:element>
+        </xsd:schema>
+    """)
+
+    schema = xsd.Schema(node)
+
+    node = load_xml("""
+          <container xmlns="https://tests.python-zeep.org">
+            <something>
+              <contains>text</contains>
+            </something>
+          </container>
+    """)
+
+    elm = schema.get_element('ns0:container')
+    elm.parse(node, schema)
+
+
+def test_element_any_type():
+    node = etree.fromstring("""
+        <?xml version="1.0"?>
+        <schema xmlns="http://www.w3.org/2001/XMLSchema"
+                xmlns:tns="http://tests.python-zeep.org/"
+                targetNamespace="http://tests.python-zeep.org/"
+                elementFormDefault="qualified">
+          <element name="container">
+            <complexType>
+              <sequence>
+                <element name="something" type="anyType"/>
+              </sequence>
+            </complexType>
+          </element>
+        </schema>
+    """.strip())
+    schema = xsd.Schema(node)
+
+    container_elm = schema.get_element('{http://tests.python-zeep.org/}container')
+    obj = container_elm(something='bar')
+
+    node = etree.Element('document')
+    container_elm.render(node, obj)
+    expected = """
+        <document>
+            <ns0:container xmlns:ns0="http://tests.python-zeep.org/">
+                <ns0:something>bar</ns0:something>
+            </ns0:container>
+        </document>
+    """
+    assert_nodes_equal(expected, node)
+    item = container_elm.parse(node.getchildren()[0], schema)
+    assert item.something == 'bar'
+
+
+def test_any_in_nested_sequence():
+    schema = xsd.Schema(load_xml("""
+        <?xml version="1.0"?>
+        <xsd:schema
+            xmlns:tns="http://tests.python-zeep.org/"
+            xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+            elementFormDefault="qualified"
+            targetNamespace="http://tests.python-zeep.org/"
+        >
+          <xsd:element name="something" type="xsd:date"/>
+          <xsd:element name="foobar" type="xsd:boolean"/>
+          <xsd:element name="container">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="items" minOccurs="0">
+                  <xsd:complexType>
+                    <xsd:sequence>
+                      <xsd:any namespace="##any" processContents="lax"/>
+                    </xsd:sequence>
+                  </xsd:complexType>
+                </xsd:element>
+                <xsd:element name="version" type="xsd:string"/>
+                <xsd:any namespace="##any" processContents="lax" minOccurs="0" maxOccurs="unbounded"/>
+              </xsd:sequence>
+            </xsd:complexType>
+          </xsd:element>
+        </xsd:schema>
+    """))   # noqa
+
+    container_elm = schema.get_element('{http://tests.python-zeep.org/}container')
+    assert container_elm.signature() == (
+        'items: {_value_1: ANY}, version: xsd:string, _value_1: ANY[]')
+
+    something = schema.get_element('{http://tests.python-zeep.org/}something')
+    foobar = schema.get_element('{http://tests.python-zeep.org/}foobar')
+
+    any_1 = xsd.AnyObject(something, datetime.date(2016, 7, 4))
+    any_2 = xsd.AnyObject(foobar, True)
+    obj = container_elm(
+        items={'_value_1': any_1}, version='str1234', _value_1=[any_1, any_2])
+
+    node = etree.Element('document')
+    container_elm.render(node, obj)
+    expected = """
+        <document>
+          <ns0:container xmlns:ns0="http://tests.python-zeep.org/">
+            <ns0:items>
+              <ns0:something>2016-07-04</ns0:something>
+            </ns0:items>
+            <ns0:version>str1234</ns0:version>
+            <ns0:something>2016-07-04</ns0:something>
+            <ns0:foobar>true</ns0:foobar>
+          </ns0:container>
+        </document>
+    """
+    assert_nodes_equal(expected, node)
+    item = container_elm.parse(node.getchildren()[0], schema)
+    assert item.items._value_1 == datetime.date(2016, 7, 4)
+    assert item.version == 'str1234'
+    assert item._value_1 == [datetime.date(2016, 7, 4), True]
diff --git a/tests/test_xsd_attributes.py b/tests/test_xsd_attributes.py
new file mode 100644
index 0000000..3e42458
--- /dev/null
+++ b/tests/test_xsd_attributes.py
@@ -0,0 +1,420 @@
+from collections import OrderedDict
+
+from lxml import etree
+
+from tests.utils import assert_nodes_equal, load_xml
+from zeep import xsd
+
+
+def test_anyattribute():
+    schema = xsd.Schema(load_xml("""
+        <?xml version="1.0"?>
+        <schema xmlns="http://www.w3.org/2001/XMLSchema"
+                xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+                xmlns:tns="http://tests.python-zeep.org/"
+                elementFormDefault="qualified"
+                targetNamespace="http://tests.python-zeep.org/">
+          <element name="container">
+            <complexType>
+              <sequence>
+                <element name="foo" type="xsd:string" />
+              </sequence>
+              <xsd:anyAttribute processContents="lax"/>
+            </complexType>
+          </element>
+        </schema>
+    """))
+
+    container_elm = schema.get_element('{http://tests.python-zeep.org/}container')
+    assert container_elm.signature() == (
+        'foo: xsd:string, _attr_1: {}')
+    obj = container_elm(foo='bar', _attr_1=OrderedDict([
+        ('hiep', 'hoi'), ('hoi', 'hiep')
+    ]))
+
+    expected = """
+      <document>
+        <ns0:container xmlns:ns0="http://tests.python-zeep.org/" hiep="hoi" hoi="hiep">
+          <ns0:foo>bar</ns0:foo>
+        </ns0:container>
+      </document>
+    """
+
+    node = etree.Element('document')
+    container_elm.render(node, obj)
+    assert_nodes_equal(expected, node)
+
+    item = container_elm.parse(node.getchildren()[0], schema)
+    assert item._attr_1 == {'hiep': 'hoi', 'hoi': 'hiep'}
+    assert item.foo == 'bar'
+
+
+def test_attribute_list_type():
+    schema = xsd.Schema(load_xml("""
+        <?xml version="1.0"?>
+        <schema xmlns="http://www.w3.org/2001/XMLSchema"
+                xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+                xmlns:tns="http://tests.python-zeep.org/"
+                elementFormDefault="qualified"
+                targetNamespace="http://tests.python-zeep.org/">
+          <simpleType name="list">
+            <list itemType="int"/>
+          </simpleType>
+
+          <element name="container">
+            <complexType>
+              <sequence>
+                <element name="foo" type="xsd:string" />
+              </sequence>
+              <xsd:attribute name="lijst" type="tns:list"/>
+            </complexType>
+          </element>
+        </schema>
+    """))
+
+    container_elm = schema.get_element('{http://tests.python-zeep.org/}container')
+    assert container_elm.signature() == ('foo: xsd:string, lijst: xsd:int[]')
+    obj = container_elm(foo='bar', lijst=[1, 2, 3])
+    expected = """
+      <document>
+        <ns0:container xmlns:ns0="http://tests.python-zeep.org/" lijst="1 2 3">
+          <ns0:foo>bar</ns0:foo>
+        </ns0:container>
+      </document>
+    """
+
+    node = etree.Element('document')
+    container_elm.render(node, obj)
+    assert_nodes_equal(expected, node)
+
+    item = container_elm.parse(node.getchildren()[0], schema)
+    assert item.lijst == [1, 2, 3]
+    assert item.foo == 'bar'
+
+
+def test_ref_attribute_qualified():
+    schema = xsd.Schema(load_xml("""
+        <?xml version="1.0"?>
+        <xsd:schema
+                xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+                xmlns:tns="http://tests.python-zeep.org/"
+                attributeFormDefault="qualified"
+                targetNamespace="http://tests.python-zeep.org/">
+          <xsd:element name="container">
+            <xsd:complexType>
+              <xsd:attribute ref="tns:attr" use="required" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:attribute name="attr" type="xsd:string" />
+        </xsd:schema>
+    """))
+
+    elm_cls = schema.get_element('{http://tests.python-zeep.org/}container')
+    instance = elm_cls(attr="hoi")
+
+    expected = """
+      <document>
+        <ns0:container xmlns:ns0="http://tests.python-zeep.org/" ns0:attr="hoi"/>
+      </document>
+    """
+
+    node = etree.Element('document')
+    elm_cls.render(node, instance)
+    assert_nodes_equal(expected, node)
+
+
+def test_ref_attribute_unqualified():
+    schema = xsd.Schema(load_xml("""
+        <?xml version="1.0"?>
+        <xsd:schema
+                xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+                xmlns:tns="http://tests.python-zeep.org/"
+                attributeFormDefault="unqualified"
+                targetNamespace="http://tests.python-zeep.org/">
+          <xsd:element name="container">
+            <xsd:complexType>
+              <xsd:attribute ref="tns:attr" use="required" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:attribute name="attr" type="xsd:string" />
+        </xsd:schema>
+    """))
+
+    elm_cls = schema.get_element('{http://tests.python-zeep.org/}container')
+    instance = elm_cls(attr="hoi")
+
+    expected = """
+      <document>
+        <ns0:container xmlns:ns0="http://tests.python-zeep.org/" ns0:attr="hoi"/>
+      </document>
+    """
+
+    node = etree.Element('document')
+    elm_cls.render(node, instance)
+    assert_nodes_equal(expected, node)
+
+
+def test_complex_type_with_attributes():
+    schema = xsd.Schema(load_xml("""
+        <?xml version="1.0"?>
+        <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
+          <xs:complexType name="Address">
+            <xs:sequence>
+              <xs:element minOccurs="0" maxOccurs="1" name="NameFirst" type="xs:string"/>
+              <xs:element minOccurs="0" maxOccurs="1" name="NameLast" type="xs:string"/>
+              <xs:element minOccurs="0" maxOccurs="1" name="Email" type="xs:string"/>
+            </xs:sequence>
+            <xs:attribute name="id" type="xs:string" use="required"/>
+          </xs:complexType>
+          <xs:element name="Address" type="Address"/>
+        </xs:schema>
+    """))
+
+    address_type = schema.get_element('Address')
+    obj = address_type(
+        NameFirst='John', NameLast='Doe', Email='j.doe at example.com', id='123')
+
+    node = etree.Element('document')
+    address_type.render(node, obj)
+
+    expected = """
+        <document>
+            <Address id="123">
+                <NameFirst>John</NameFirst>
+                <NameLast>Doe</NameLast>
+                <Email>j.doe at example.com</Email>
+            </Address>
+        </document>
+    """
+    assert_nodes_equal(expected, node)
+
+
+def test_qualified_attribute():
+    schema = xsd.Schema(load_xml("""
+        <?xml version="1.0"?>
+        <xsd:schema
+                xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+                xmlns:tns="http://tests.python-zeep.org/"
+                attributeFormDefault="qualified"
+                elementFormDefault="qualified"
+                targetNamespace="http://tests.python-zeep.org/">
+          <xsd:element name="Address">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="foo" type="xsd:string" form="unqualified" />
+              </xsd:sequence>
+              <xsd:attribute name="id" type="xsd:string" use="required" form="unqualified" />
+              <xsd:attribute name="pos" type="xsd:string" use="required" />
+            </xsd:complexType>
+          </xsd:element>
+        </xsd:schema>
+    """))
+
+    address_type = schema.get_element('{http://tests.python-zeep.org/}Address')
+    obj = address_type(foo='bar', id="20", pos="30")
+
+    expected = """
+      <document>
+        <ns0:Address xmlns:ns0="http://tests.python-zeep.org/" id="20" ns0:pos="30">
+          <foo>bar</foo>
+        </ns0:Address>
+      </document>
+    """
+
+    node = etree.Element('document')
+    address_type.render(node, obj)
+    assert_nodes_equal(expected, node)
+
+
+def test_group():
+    schema = xsd.Schema(load_xml("""
+        <?xml version="1.0"?>
+        <xsd:schema
+                xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+                xmlns:tns="http://tests.python-zeep.org/"
+                attributeFormDefault="qualified"
+                elementFormDefault="qualified"
+                targetNamespace="http://tests.python-zeep.org/">
+
+          <xsd:attributeGroup name="groepje">
+            <xsd:attribute name="id" type="xsd:string" use="required" form="unqualified" />
+            <xsd:attribute name="pos" type="xsd:string" use="required" />
+          </xsd:attributeGroup>
+          <xsd:element name="Address">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="foo" type="xsd:string" form="unqualified" />
+              </xsd:sequence>
+              <xsd:attributeGroup ref="tns:groepje"/>
+            </xsd:complexType>
+          </xsd:element>
+        </xsd:schema>
+    """))
+
+    address_type = schema.get_element('{http://tests.python-zeep.org/}Address')
+    obj = address_type(foo='bar', id="20", pos="30")
+
+    expected = """
+      <document>
+        <ns0:Address xmlns:ns0="http://tests.python-zeep.org/" id="20" ns0:pos="30">
+          <foo>bar</foo>
+        </ns0:Address>
+      </document>
+    """
+
+    node = etree.Element('document')
+    address_type.render(node, obj)
+    assert_nodes_equal(expected, node)
+
+
+def test_group_nested():
+    schema = xsd.Schema(load_xml("""
+        <?xml version="1.0"?>
+        <xsd:schema
+                xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+                xmlns:tns="http://tests.python-zeep.org/"
+                attributeFormDefault="qualified"
+                elementFormDefault="qualified"
+                targetNamespace="http://tests.python-zeep.org/">
+
+          <xsd:attributeGroup name="groepje">
+            <xsd:attribute name="id" type="xsd:string" use="required" form="unqualified" />
+            <xsd:attribute name="pos" type="xsd:string" use="required" />
+            <xsd:attributeGroup ref="tns:nestje"/>
+          </xsd:attributeGroup>
+
+          <xsd:attributeGroup name="nestje">
+            <xsd:attribute name="size" type="xsd:string" use="required" />
+          </xsd:attributeGroup>
+
+          <xsd:element name="Address">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="foo" type="xsd:string" form="unqualified" />
+              </xsd:sequence>
+              <xsd:attributeGroup ref="tns:groepje"/>
+            </xsd:complexType>
+          </xsd:element>
+        </xsd:schema>
+    """))
+
+    address_type = schema.get_element('{http://tests.python-zeep.org/}Address')
+    obj = address_type(foo='bar', id="20", pos="30", size="maat")
+
+    expected = """
+      <document>
+        <ns0:Address
+            xmlns:ns0="http://tests.python-zeep.org/" id="20" ns0:pos="30" ns0:size="maat">
+          <foo>bar</foo>
+        </ns0:Address>
+      </document>
+    """
+
+    node = etree.Element('document')
+    address_type.render(node, obj)
+    assert_nodes_equal(expected, node)
+
+
+def test_nested_attribute():
+    schema = xsd.Schema(load_xml("""
+        <?xml version="1.0"?>
+        <schema xmlns="http://www.w3.org/2001/XMLSchema"
+                xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+                xmlns:tns="http://tests.python-zeep.org/"
+                elementFormDefault="qualified"
+                targetNamespace="http://tests.python-zeep.org/">
+          <element name="container">
+            <complexType>
+              <sequence>
+                <element name="item">
+                    <complexType>
+                        <sequence>
+                            <element name="x" type="xsd:string"/>
+                        </sequence>
+                        <attribute name="y" type="xsd:string"/>
+                    </complexType>
+                </element>
+              </sequence>
+            </complexType>
+          </element>
+        </schema>
+    """))
+
+    container_elm = schema.get_element('{http://tests.python-zeep.org/}container')
+    assert container_elm.signature() == 'item: {x: xsd:string, y: xsd:string}'
+    obj = container_elm(item={'x': 'foo', 'y': 'bar'})
+
+    expected = """
+      <document>
+        <ns0:container xmlns:ns0="http://tests.python-zeep.org/">
+          <ns0:item y="bar">
+            <ns0:x>foo</ns0:x>
+          </ns0:item>
+        </ns0:container>
+      </document>
+    """
+
+    node = etree.Element('document')
+    container_elm.render(node, obj)
+    assert_nodes_equal(expected, node)
+
+
+def test_attribute_union_type():
+    schema = xsd.Schema(load_xml("""
+        <?xml version="1.0"?>
+        <schema xmlns="http://www.w3.org/2001/XMLSchema"
+                xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+                xmlns:tns="http://tests.python-zeep.org/"
+                elementFormDefault="qualified"
+                targetNamespace="http://tests.python-zeep.org/">
+          <simpleType name="one">
+            <restriction base="xsd:string"/>
+          </simpleType>
+          <simpleType name="parent">
+            <union memberTypes="one two"/>
+          </simpleType>
+          <simpleType name="two">
+            <restriction base="xsd:string"/>
+          </simpleType>
+          <attribute name="something" type="tns:something"/>
+          <simpleType name="something">
+            <restriction base="tns:parent">
+              <enumeration value="preserve"/>
+            </restriction>
+          </simpleType>
+        </schema>
+    """))
+
+    attr = schema.get_attribute('{http://tests.python-zeep.org/}something')
+    assert attr('foo') == 'foo'
+
+
+def test_attribute_union_type_inline():
+    schema = xsd.Schema(load_xml("""
+        <?xml version="1.0"?>
+        <schema xmlns="http://www.w3.org/2001/XMLSchema"
+                xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+                xmlns:tns="http://tests.python-zeep.org/"
+                elementFormDefault="qualified"
+                targetNamespace="http://tests.python-zeep.org/">
+          <simpleType name="parent">
+            <union>
+              <simpleType name="one">
+                <restriction base="xsd:string"/>
+              </simpleType>
+              <simpleType name="two">
+                <restriction base="xsd:string"/>
+              </simpleType>
+            </union>
+          </simpleType>
+          <attribute name="something" type="tns:something"/>
+          <simpleType name="something">
+            <restriction base="tns:parent">
+              <enumeration value="preserve"/>
+            </restriction>
+          </simpleType>
+        </schema>
+    """))
+
+    attr = schema.get_attribute('{http://tests.python-zeep.org/}something')
+    assert attr('foo') == 'foo'
diff --git a/tests/test_xsd_builtins.py b/tests/test_xsd_builtins.py
index 0d3da42..a10951d 100644
--- a/tests/test_xsd_builtins.py
+++ b/tests/test_xsd_builtins.py
@@ -122,6 +122,10 @@ class TestDateTime:
         value = datetime.datetime(2016, 3, 4, 21, 14, 42, tzinfo=pytz.utc)
         assert instance.xmlvalue(value) == '2016-03-04T21:14:42Z'
 
+        value = datetime.datetime(2016, 3, 4, 21, 14, 42, 123456, tzinfo=pytz.utc)
+        assert instance.xmlvalue(value) == '2016-03-04T21:14:42.123456Z'
+
+        value = datetime.datetime(2016, 3, 4, 21, 14, 42, tzinfo=pytz.utc)
         value = value.astimezone(pytz.timezone('Europe/Amsterdam'))
         assert instance.xmlvalue(value) == '2016-03-04T22:14:42+01:00'
 
@@ -130,6 +134,9 @@ class TestDateTime:
         value = datetime.datetime(2016, 3, 4, 21, 14, 42)
         assert instance.pythonvalue('2016-03-04T21:14:42') == value
 
+        value = datetime.datetime(2016, 3, 4, 21, 14, 42, 123456)
+        assert instance.pythonvalue('2016-03-04T21:14:42.123456') == value
+
     def test_pythonvalue_invalid(self):
         instance = builtins.DateTime()
         with pytest.raises(ValueError):
diff --git a/tests/test_xsd_choice.py b/tests/test_xsd_choice.py
new file mode 100644
index 0000000..7478962
--- /dev/null
+++ b/tests/test_xsd_choice.py
@@ -0,0 +1,979 @@
+import pytest
+from lxml import etree
+
+from tests.utils import assert_nodes_equal, load_xml, render_node
+from zeep import xsd
+from zeep.exceptions import XMLParseError
+from zeep.helpers import serialize_object
+
+
+def test_choice_element():
+    node = etree.fromstring("""
+        <?xml version="1.0"?>
+        <xsd:schema
+                xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+                xmlns:tns="http://tests.python-zeep.org/"
+                elementFormDefault="qualified"
+                targetNamespace="http://tests.python-zeep.org/">
+          <xsd:element name="container">
+            <xsd:complexType>
+              <xsd:choice>
+                <xsd:element name="item_1" type="xsd:string" />
+                <xsd:element name="item_2" type="xsd:string" />
+                <xsd:element name="item_3" type="xsd:string" />
+              </xsd:choice>
+            </xsd:complexType>
+          </xsd:element>
+        </xsd:schema>
+    """.strip())
+    schema = xsd.Schema(node)
+    element = schema.get_element('ns0:container')
+
+    value = element(item_1="foo")
+    assert value.item_1 == 'foo'
+    assert value.item_2 is None
+    assert value.item_3 is None
+
+    expected = """
+      <document>
+        <ns0:container xmlns:ns0="http://tests.python-zeep.org/">
+          <ns0:item_1>foo</ns0:item_1>
+        </ns0:container>
+      </document>
+    """
+    node = etree.Element('document')
+    element.render(node, value)
+    assert_nodes_equal(expected, node)
+
+    value = element.parse(node.getchildren()[0], schema)
+    assert value.item_1 == 'foo'
+    assert value.item_2 is None
+    assert value.item_3 is None
+
+
+def test_choice_element_second_elm():
+    node = etree.fromstring("""
+        <?xml version="1.0"?>
+        <xsd:schema
+                xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+                xmlns:tns="http://tests.python-zeep.org/"
+                elementFormDefault="qualified"
+                targetNamespace="http://tests.python-zeep.org/">
+          <xsd:element name="container">
+            <xsd:complexType>
+              <xsd:choice>
+                <xsd:element name="item_1" type="xsd:string" />
+                <xsd:element name="item_2" type="xsd:string" />
+                <xsd:element name="item_3" type="xsd:string" />
+              </xsd:choice>
+            </xsd:complexType>
+          </xsd:element>
+        </xsd:schema>
+    """.strip())
+    schema = xsd.Schema(node)
+    element = schema.get_element('ns0:container')
+
+    value = element(item_2="foo")
+    assert value.item_1 is None
+    assert value.item_2 == 'foo'
+    assert value.item_3 is None
+
+    expected = """
+      <document>
+        <ns0:container xmlns:ns0="http://tests.python-zeep.org/">
+          <ns0:item_2>foo</ns0:item_2>
+        </ns0:container>
+      </document>
+    """
+    node = etree.Element('document')
+    element.render(node, value)
+    assert_nodes_equal(expected, node)
+
+    value = element.parse(node.getchildren()[0], schema)
+    assert value.item_1 is None
+    assert value.item_2 == 'foo'
+    assert value.item_3 is None
+
+
+def test_choice_element_multiple():
+    node = etree.fromstring("""
+        <?xml version="1.0"?>
+        <xsd:schema
+                xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+                xmlns:tns="http://tests.python-zeep.org/"
+                elementFormDefault="qualified"
+                targetNamespace="http://tests.python-zeep.org/">
+          <xsd:element name="container">
+            <xsd:complexType>
+              <xsd:choice maxOccurs="3">
+                <xsd:element name="item_1" type="xsd:string" />
+                <xsd:element name="item_2" type="xsd:string" />
+                <xsd:element name="item_3" type="xsd:string" />
+              </xsd:choice>
+            </xsd:complexType>
+          </xsd:element>
+        </xsd:schema>
+    """.strip())
+    schema = xsd.Schema(node)
+    element = schema.get_element('ns0:container')
+
+    value = element(_value_1=[
+        {'item_1': 'foo'}, {'item_2': 'bar'}, {'item_1': 'three'},
+    ])
+    assert value._value_1 == [
+        {'item_1': 'foo'}, {'item_2': 'bar'}, {'item_1': 'three'},
+    ]
+
+    expected = """
+      <document>
+        <ns0:container xmlns:ns0="http://tests.python-zeep.org/">
+          <ns0:item_1>foo</ns0:item_1>
+          <ns0:item_2>bar</ns0:item_2>
+          <ns0:item_1>three</ns0:item_1>
+        </ns0:container>
+      </document>
+    """
+    node = etree.Element('document')
+    element.render(node, value)
+    assert_nodes_equal(expected, node)
+
+    value = element.parse(node.getchildren()[0], schema)
+    assert value._value_1 == [
+        {'item_1': 'foo'}, {'item_2': 'bar'}, {'item_1': 'three'},
+    ]
+
+
+def test_choice_element_optional():
+    node = etree.fromstring("""
+        <?xml version="1.0"?>
+        <xsd:schema
+                xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+                xmlns:tns="http://tests.python-zeep.org/"
+                elementFormDefault="qualified"
+                targetNamespace="http://tests.python-zeep.org/">
+          <xsd:element name="container">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:choice minOccurs="0">
+                  <xsd:element name="item_1" type="xsd:string" />
+                  <xsd:element name="item_2" type="xsd:string" />
+                  <xsd:element name="item_3" type="xsd:string" />
+                </xsd:choice>
+                <xsd:element name="item_4" type="xsd:string" />
+             </xsd:sequence>
+            </xsd:complexType>
+          </xsd:element>
+        </xsd:schema>
+    """.strip())
+    schema = xsd.Schema(node)
+    element = schema.get_element('ns0:container')
+    value = element(item_4="foo")
+
+    expected = """
+      <document>
+        <ns0:container xmlns:ns0="http://tests.python-zeep.org/">
+          <ns0:item_4>foo</ns0:item_4>
+        </ns0:container>
+      </document>
+    """
+    node = etree.Element('document')
+    element.render(node, value)
+    assert_nodes_equal(expected, node)
+
+
+def test_choice_element_with_any():
+    node = etree.fromstring("""
+        <?xml version="1.0"?>
+        <xsd:schema
+                xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+                xmlns:tns="http://tests.python-zeep.org/"
+                elementFormDefault="qualified"
+                targetNamespace="http://tests.python-zeep.org/">
+          <xsd:element name="container">
+            <xsd:complexType>
+              <xsd:choice minOccurs="0">
+                <xsd:element name="item_1" type="xsd:string" />
+                <xsd:element name="item_2" type="xsd:string" />
+                <xsd:element name="item_3" type="xsd:string" />
+                <xsd:any namespace="##other" minOccurs="0" maxOccurs="unbounded"/>
+              </xsd:choice>
+              <xsd:attribute name="name" type="xsd:QName" use="required" />
+              <xsd:attribute name="something" type="xsd:boolean" use="required" />
+              <xsd:anyAttribute namespace="##other" processContents="lax"/>
+            </xsd:complexType>
+          </xsd:element>
+        </xsd:schema>
+    """.strip())
+    schema = xsd.Schema(node)
+    element = schema.get_element('ns0:container')
+    value = element(item_1="foo", name="foo", something="bar")
+
+    expected = """
+      <document>
+        <ns0:container xmlns:ns0="http://tests.python-zeep.org/" name="foo" something="true">
+          <ns0:item_1>foo</ns0:item_1>
+        </ns0:container>
+      </document>
+    """
+    node = etree.Element('document')
+    element.render(node, value)
+    assert_nodes_equal(expected, node)
+
+    result = element.parse(node.getchildren()[0], schema)
+    assert result.name == 'foo'
+    assert result.something is True
+    assert result.item_1 == 'foo'
+
+
+def test_choice_element_with_any_max_occurs():
+    schema = xsd.Schema(load_xml("""
+        <schema targetNamespace="http://tests.python-zeep.org/"
+            xmlns="http://www.w3.org/2001/XMLSchema"
+            xmlns:tns="http://tests.python-zeep.org/"
+            elementFormDefault="qualified">
+
+          <element name="item_any" type="string"/>
+          <element name="container">
+            <complexType>
+                <sequence>
+                  <choice minOccurs="0">
+                    <element maxOccurs="999" minOccurs="0" name="item_1" type="string"/>
+                    <sequence>
+                      <element minOccurs="0" name="item_2"/>
+                      <any maxOccurs="unbounded" minOccurs="0"/>
+                    </sequence>
+                  </choice>
+                </sequence>
+            </complexType>
+          </element>
+        </schema>
+    """))
+
+    element = schema.get_element('ns0:container')
+    value = element(
+        item_2="item-2",
+        _value_1=[
+            xsd.AnyObject(schema.get_element('ns0:item_any'), 'any-content')
+        ])
+
+    expected = """
+        <document>
+          <ns0:container xmlns:ns0="http://tests.python-zeep.org/">
+            <ns0:item_2>item-2</ns0:item_2>
+            <ns0:item_any>any-content</ns0:item_any>
+          </ns0:container>
+        </document>
+    """
+    node = render_node(element, value)
+    assert_nodes_equal(node, expected)
+    result = element.parse(node.getchildren()[0], schema)
+    assert result.item_2 == 'item-2'
+    assert result._value_1 == ['any-content']
+
+
+def test_choice_optional_values():
+    schema = load_xml("""
+        <xsd:schema
+            xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+            xmlns:tns="http://tests.python-zeep.org/"
+            targetNamespace="http://tests.python-zeep.org/"
+            elementFormDefault="qualified">
+          <xsd:complexType name="Transport">
+            <xsd:sequence>
+                <xsd:choice minOccurs="0" maxOccurs="1">
+                    <xsd:element name="item" type="xsd:string"/>
+                </xsd:choice>
+            </xsd:sequence>
+          </xsd:complexType>
+        </xsd:schema>
+    """)
+    schema = xsd.Schema(schema)
+
+    node = load_xml("<Transport></Transport>")
+    elm = schema.get_type('ns0:Transport')
+    elm.parse_xmlelement(node, schema)
+
+
+def test_choice_in_sequence():
+    node = etree.fromstring("""
+        <?xml version="1.0"?>
+        <xsd:schema
+                xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+                xmlns:tns="http://tests.python-zeep.org/"
+                elementFormDefault="qualified"
+                targetNamespace="http://tests.python-zeep.org/">
+          <xsd:element name="container">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="something" type="xsd:string" />
+                <xsd:choice>
+                  <xsd:element name="item_1" type="xsd:string" />
+                  <xsd:element name="item_2" type="xsd:string" />
+                  <xsd:element name="item_3" type="xsd:string" />
+                </xsd:choice>
+              </xsd:sequence>
+            </xsd:complexType>
+          </xsd:element>
+        </xsd:schema>
+    """.strip())
+    schema = xsd.Schema(node)
+    container_elm = schema.get_element('ns0:container')
+
+    assert container_elm.type.signature() == (
+        'something: xsd:string, ({item_1: xsd:string} | {item_2: xsd:string} | {item_3: xsd:string})')  # noqa
+    value = container_elm(item_1='item-1')
+
+    expected = """
+      <document>
+        <ns0:container xmlns:ns0="http://tests.python-zeep.org/">
+          <ns0:something/>
+          <ns0:item_1>item-1</ns0:item_1>
+        </ns0:container>
+      </document>
+    """
+    node = etree.Element('document')
+    container_elm.render(node, value)
+    assert_nodes_equal(expected, node)
+
+
+def test_choice_with_sequence():
+    node = load_xml("""
+        <?xml version="1.0"?>
+        <xsd:schema
+                xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+                xmlns:tns="http://tests.python-zeep.org/"
+                elementFormDefault="qualified"
+                targetNamespace="http://tests.python-zeep.org/">
+          <xsd:element name="container">
+            <xsd:complexType xmlns:xsd="http://www.w3.org/2001/XMLSchema">
+              <xsd:choice>
+                <xsd:sequence>
+                    <xsd:element name="item_1" type="xsd:string"/>
+                    <xsd:element name="item_2" type="xsd:string"/>
+                </xsd:sequence>
+                <xsd:sequence>
+                    <xsd:element name="item_3" type="xsd:string"/>
+                    <xsd:element name="item_4" type="xsd:string"/>
+                </xsd:sequence>
+              </xsd:choice>
+            </xsd:complexType>
+          </xsd:element>
+        </xsd:schema>
+    """)
+    schema = xsd.Schema(node)
+    element = schema.get_element('ns0:container')
+    assert element.type.signature() == (
+        '({item_1: xsd:string, item_2: xsd:string} | {item_3: xsd:string, item_4: xsd:string})')
+    value = element(item_1='foo', item_2='bar')
+
+    expected = """
+      <document>
+        <ns0:container xmlns:ns0="http://tests.python-zeep.org/">
+          <ns0:item_1>foo</ns0:item_1>
+          <ns0:item_2>bar</ns0:item_2>
+        </ns0:container>
+      </document>
+    """
+    node = etree.Element('document')
+    element.render(node, value)
+    assert_nodes_equal(expected, node)
+
+
+def test_choice_with_sequence_once():
+    node = load_xml("""
+        <?xml version="1.0"?>
+        <xsd:schema
+                xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+                xmlns:tns="http://tests.python-zeep.org/"
+                elementFormDefault="qualified"
+                targetNamespace="http://tests.python-zeep.org/">
+          <xsd:element name="container">
+            <xsd:complexType xmlns:xsd="http://www.w3.org/2001/XMLSchema">
+              <xsd:sequence>
+                <xsd:element name="item_0" type="xsd:string"/>
+                <xsd:choice>
+                  <xsd:sequence>
+                      <xsd:element name="item_1" type="xsd:string"/>
+                      <xsd:element name="item_2" type="xsd:string"/>
+                  </xsd:sequence>
+                </xsd:choice>
+              </xsd:sequence>
+            </xsd:complexType>
+          </xsd:element>
+        </xsd:schema>
+    """)
+    schema = xsd.Schema(node)
+    element = schema.get_element('ns0:container')
+    assert element.type.signature() == (
+        'item_0: xsd:string, ({item_1: xsd:string, item_2: xsd:string})')
+    value = element(item_0='nul', item_1='foo', item_2='bar')
+
+    expected = """
+      <document>
+        <ns0:container xmlns:ns0="http://tests.python-zeep.org/">
+          <ns0:item_0>nul</ns0:item_0>
+          <ns0:item_1>foo</ns0:item_1>
+          <ns0:item_2>bar</ns0:item_2>
+        </ns0:container>
+      </document>
+    """
+    node = etree.Element('document')
+    element.render(node, value)
+    assert_nodes_equal(expected, node)
+
+
+def test_choice_with_sequence_once_extra_data():
+    node = load_xml("""
+        <?xml version="1.0"?>
+        <xsd:schema
+                xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+                xmlns:tns="http://tests.python-zeep.org/"
+                elementFormDefault="qualified"
+                targetNamespace="http://tests.python-zeep.org/">
+          <xsd:element name="container">
+            <xsd:complexType xmlns:xsd="http://www.w3.org/2001/XMLSchema">
+              <xsd:sequence>
+                <xsd:element name="item_0" type="xsd:string"/>
+                <xsd:choice>
+                  <xsd:sequence>
+                      <xsd:element name="item_1" type="xsd:string"/>
+                      <xsd:element name="item_2" type="xsd:string"/>
+                  </xsd:sequence>
+                </xsd:choice>
+                <xsd:element name="item_3" type="xsd:string"/>
+              </xsd:sequence>
+            </xsd:complexType>
+          </xsd:element>
+        </xsd:schema>
+    """)
+    schema = xsd.Schema(node)
+    element = schema.get_element('ns0:container')
+    assert element.type.signature() == (
+        'item_0: xsd:string, ({item_1: xsd:string, item_2: xsd:string}), item_3: xsd:string')
+    value = element(item_0='nul', item_1='foo', item_2='bar', item_3='item-3')
+
+    expected = """
+      <document>
+        <ns0:container xmlns:ns0="http://tests.python-zeep.org/">
+          <ns0:item_0>nul</ns0:item_0>
+          <ns0:item_1>foo</ns0:item_1>
+          <ns0:item_2>bar</ns0:item_2>
+          <ns0:item_3>item-3</ns0:item_3>
+        </ns0:container>
+      </document>
+    """
+    node = etree.Element('document')
+    element.render(node, value)
+    assert_nodes_equal(expected, node)
+
+
+def test_choice_with_sequence_second():
+    node = load_xml("""
+        <?xml version="1.0"?>
+        <xsd:schema
+                xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+                xmlns:tns="http://tests.python-zeep.org/"
+                elementFormDefault="qualified"
+                targetNamespace="http://tests.python-zeep.org/">
+          <xsd:element name="container">
+            <xsd:complexType xmlns:xsd="http://www.w3.org/2001/XMLSchema">
+              <xsd:choice>
+                <xsd:sequence>
+                    <xsd:element name="item_1" type="xsd:string"/>
+                    <xsd:element name="item_2" type="xsd:string"/>
+                </xsd:sequence>
+                <xsd:sequence>
+                    <xsd:element name="item_3" type="xsd:string"/>
+                    <xsd:element name="item_4" type="xsd:string"/>
+                </xsd:sequence>
+              </xsd:choice>
+            </xsd:complexType>
+          </xsd:element>
+        </xsd:schema>
+    """)
+    schema = xsd.Schema(node)
+    element = schema.get_element('ns0:container')
+    assert element.type.signature() == (
+        '({item_1: xsd:string, item_2: xsd:string} | {item_3: xsd:string, item_4: xsd:string})')
+    value = element(item_3='foo', item_4='bar')
+
+    expected = """
+      <document>
+        <ns0:container xmlns:ns0="http://tests.python-zeep.org/">
+          <ns0:item_3>foo</ns0:item_3>
+          <ns0:item_4>bar</ns0:item_4>
+        </ns0:container>
+      </document>
+    """
+    node = etree.Element('document')
+    element.render(node, value)
+    assert_nodes_equal(expected, node)
+
+
+def test_choice_with_sequence_invalid():
+    node = load_xml("""
+        <?xml version="1.0"?>
+        <xsd:schema
+                xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+                xmlns:tns="http://tests.python-zeep.org/"
+                elementFormDefault="qualified"
+                targetNamespace="http://tests.python-zeep.org/">
+          <xsd:element name="container">
+            <xsd:complexType xmlns:xsd="http://www.w3.org/2001/XMLSchema">
+              <xsd:choice>
+                <xsd:sequence>
+                    <xsd:element name="item_1" type="xsd:string"/>
+                    <xsd:element name="item_2" type="xsd:string"/>
+                </xsd:sequence>
+                <xsd:sequence>
+                    <xsd:element name="item_3" type="xsd:string"/>
+                    <xsd:element name="item_4" type="xsd:string"/>
+                </xsd:sequence>
+              </xsd:choice>
+            </xsd:complexType>
+          </xsd:element>
+        </xsd:schema>
+    """)
+    schema = xsd.Schema(node)
+    element = schema.get_element('ns0:container')
+    assert element.type.signature() == (
+        '({item_1: xsd:string, item_2: xsd:string} | {item_3: xsd:string, item_4: xsd:string})')
+
+    with pytest.raises(TypeError):
+        element(item_1='foo', item_4='bar')
+
+
+def test_choice_with_sequence_change():
+    node = load_xml("""
+        <?xml version="1.0"?>
+        <xsd:schema
+                xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+                xmlns:tns="http://tests.python-zeep.org/"
+                elementFormDefault="qualified"
+                targetNamespace="http://tests.python-zeep.org/">
+          <xsd:element name='ElementName'>
+            <xsd:complexType xmlns:xsd="http://www.w3.org/2001/XMLSchema">
+              <xsd:choice>
+                <xsd:sequence>
+                    <xsd:element name="item_1" type="xsd:string"/>
+                    <xsd:element name="item_2" type="xsd:string"/>
+                </xsd:sequence>
+                <xsd:sequence>
+                    <xsd:element name="item_3" type="xsd:string"/>
+                    <xsd:element name="item_4" type="xsd:string"/>
+                </xsd:sequence>
+                <xsd:element name="nee" type="xsd:string"/>
+              </xsd:choice>
+            </xsd:complexType>
+          </xsd:element>
+        </xsd:schema>
+    """)
+    schema = xsd.Schema(node)
+    element = schema.get_element('ns0:ElementName')
+
+    elm = element(item_1='foo', item_2='bar')
+    assert serialize_object(elm) == {
+        'item_3': None,
+        'item_2': 'bar',
+        'item_1': 'foo',
+        'item_4': None,
+        'nee': None
+    }
+
+    elm.item_1 = 'bla-1'
+    elm.item_2 = 'bla-2'
+
+    expected = """
+      <document>
+        <ns0:ElementName xmlns:ns0="http://tests.python-zeep.org/">
+          <ns0:item_1>bla-1</ns0:item_1>
+          <ns0:item_2>bla-2</ns0:item_2>
+        </ns0:ElementName>
+      </document>
+    """
+    node = etree.Element('document')
+    element.render(node, elm)
+    assert_nodes_equal(expected, node)
+
+
+def test_choice_with_sequence_change_named():
+    node = load_xml("""
+        <?xml version="1.0"?>
+        <xsd:schema
+                xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+                xmlns:tns="http://tests.python-zeep.org/"
+                elementFormDefault="qualified"
+                targetNamespace="http://tests.python-zeep.org/">
+          <xsd:element name='ElementName'>
+            <xsd:complexType xmlns:xsd="http://www.w3.org/2001/XMLSchema">
+              <xsd:choice>
+                <xsd:sequence>
+                    <xsd:element name="item_1" type="xsd:string"/>
+                    <xsd:element name="item_2" type="xsd:string"/>
+                </xsd:sequence>
+                <xsd:element name="item_3" type="xsd:string"/>
+              </xsd:choice>
+            </xsd:complexType>
+          </xsd:element>
+        </xsd:schema>
+    """)
+    schema = xsd.Schema(node)
+    element = schema.get_element('ns0:ElementName')
+    elm = element(item_3='foo')
+    elm = element(item_1='foo', item_2='bar')
+    assert elm['item_1'] == 'foo'
+    assert elm['item_2'] == 'bar'
+
+    elm['item_1'] = 'bla-1'
+    elm['item_2'] = 'bla-2'
+
+    expected = """
+      <document>
+        <ns0:ElementName xmlns:ns0="http://tests.python-zeep.org/">
+          <ns0:item_1>bla-1</ns0:item_1>
+          <ns0:item_2>bla-2</ns0:item_2>
+        </ns0:ElementName>
+      </document>
+    """
+    node = etree.Element('document')
+    element.render(node, elm)
+    assert_nodes_equal(expected, node)
+
+
+def test_choice_with_sequence_multiple():
+    node = load_xml("""
+        <?xml version="1.0"?>
+        <xsd:schema
+                xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+                xmlns:tns="http://tests.python-zeep.org/"
+                elementFormDefault="qualified"
+                targetNamespace="http://tests.python-zeep.org/">
+          <xsd:element name="container">
+            <xsd:complexType xmlns:xsd="http://www.w3.org/2001/XMLSchema">
+              <xsd:choice maxOccurs="2">
+                <xsd:sequence>
+                    <xsd:element name="item_1" type="xsd:string"/>
+                    <xsd:element name="item_2" type="xsd:string"/>
+                </xsd:sequence>
+                <xsd:sequence>
+                    <xsd:element name="item_3" type="xsd:string"/>
+                    <xsd:element name="item_4" type="xsd:string"/>
+                </xsd:sequence>
+              </xsd:choice>
+            </xsd:complexType>
+          </xsd:element>
+        </xsd:schema>
+    """)
+    schema = xsd.Schema(node)
+    element = schema.get_element('ns0:container')
+    assert element.type.signature() == (
+        '({item_1: xsd:string, item_2: xsd:string} | {item_3: xsd:string, item_4: xsd:string})[]')
+    value = element(_value_1=[
+        dict(item_1='foo', item_2='bar'),
+        dict(item_3='foo', item_4='bar'),
+    ])
+
+    expected = """
+      <document>
+        <ns0:container xmlns:ns0="http://tests.python-zeep.org/">
+          <ns0:item_1>foo</ns0:item_1>
+          <ns0:item_2>bar</ns0:item_2>
+          <ns0:item_3>foo</ns0:item_3>
+          <ns0:item_4>bar</ns0:item_4>
+        </ns0:container>
+      </document>
+    """
+    node = etree.Element('document')
+    element.render(node, value)
+    assert_nodes_equal(expected, node)
+
+
+def test_choice_with_sequence_and_element():
+    node = load_xml("""
+        <?xml version="1.0"?>
+        <xsd:schema
+                xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+                xmlns:tns="http://tests.python-zeep.org/"
+                elementFormDefault="qualified"
+                targetNamespace="http://tests.python-zeep.org/">
+          <xsd:element name="container">
+            <xsd:complexType xmlns:xsd="http://www.w3.org/2001/XMLSchema">
+              <xsd:choice>
+                <xsd:element name="item_1" type="xsd:string"/>
+                <xsd:sequence>
+                  <xsd:choice>
+                    <xsd:element name="item_2" type="xsd:string"/>
+                    <xsd:element name="item_3" type="xsd:string"/>
+                  </xsd:choice>
+                </xsd:sequence>
+              </xsd:choice>
+            </xsd:complexType>
+          </xsd:element>
+        </xsd:schema>
+    """)
+    schema = xsd.Schema(node)
+    element = schema.get_element('ns0:container')
+    assert element.type.signature() == (
+        '({item_1: xsd:string} | {({item_2: xsd:string} | {item_3: xsd:string})})')
+
+    value = element(item_2='foo')
+
+    expected = """
+      <document>
+        <ns0:container xmlns:ns0="http://tests.python-zeep.org/">
+          <ns0:item_2>foo</ns0:item_2>
+        </ns0:container>
+      </document>
+    """
+    node = etree.Element('document')
+    element.render(node, value)
+    assert_nodes_equal(expected, node)
+
+
+def test_element_ref_in_choice():
+    node = etree.fromstring("""
+        <?xml version="1.0"?>
+        <schema xmlns="http://www.w3.org/2001/XMLSchema"
+                xmlns:tns="http://tests.python-zeep.org/"
+                targetNamespace="http://tests.python-zeep.org/"
+                   elementFormDefault="qualified">
+          <element name="foo" type="string"/>
+          <element name="bar" type="string"/>
+          <element name="container">
+            <complexType>
+              <sequence>
+                <choice>
+                  <element ref="tns:foo"/>
+                  <element ref="tns:bar"/>
+                </choice>
+              </sequence>
+            </complexType>
+          </element>
+        </schema>
+    """.strip())
+
+    schema = xsd.Schema(node)
+
+    foo_type = schema.get_element('{http://tests.python-zeep.org/}foo')
+    assert isinstance(foo_type.type, xsd.String)
+
+    custom_type = schema.get_element('{http://tests.python-zeep.org/}container')
+
+    value = custom_type(foo='bar')
+    assert value.foo == 'bar'
+    assert value.bar is None
+
+    node = etree.Element('document')
+    custom_type.render(node, value)
+    expected = """
+        <document>
+            <ns0:container xmlns:ns0="http://tests.python-zeep.org/">
+                <ns0:foo>bar</ns0:foo>
+            </ns0:container>
+        </document>
+    """
+    assert_nodes_equal(expected, node)
+
+
+def test_parse_dont_loop():
+    schema = xsd.Schema(load_xml("""
+        <?xml version="1.0"?>
+        <xsd:schema
+                xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+                xmlns:tns="http://tests.python-zeep.org/"
+                elementFormDefault="qualified"
+                targetNamespace="http://tests.python-zeep.org/">
+          <xsd:element name="container">
+            <xsd:complexType xmlns:xsd="http://www.w3.org/2001/XMLSchema">
+              <xsd:choice maxOccurs="unbounded">
+                <xsd:element name="item_1" type="xsd:string"/>
+                <xsd:element name="item_2" type="xsd:string"/>
+              </xsd:choice>
+            </xsd:complexType>
+          </xsd:element>
+        </xsd:schema>
+    """))
+
+    element = schema.get_element('ns0:container')
+    expected = load_xml("""
+        <ns0:container xmlns:ns0="http://tests.python-zeep.org/">
+          <ns0:item_1>foo</ns0:item_1>
+          <ns0:item_2>bar</ns0:item_2>
+          <ns0:item_3>foo</ns0:item_3>
+          <ns0:item_4>bar</ns0:item_4>
+        </ns0:container>
+    """)
+    with pytest.raises(XMLParseError):
+        element.parse(expected, schema)
+
+
+def test_parse_check_unexpected():
+    schema = xsd.Schema(load_xml("""
+        <?xml version="1.0"?>
+        <xsd:schema
+                xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+                xmlns:tns="http://tests.python-zeep.org/"
+                elementFormDefault="qualified"
+                targetNamespace="http://tests.python-zeep.org/">
+          <xsd:element name="container">
+            <xsd:complexType xmlns:xsd="http://www.w3.org/2001/XMLSchema">
+              <xsd:choice maxOccurs="unbounded">
+                <xsd:element name="item_1" type="xsd:string"/>
+                <xsd:element name="item_2" type="xsd:string"/>
+              </xsd:choice>
+            </xsd:complexType>
+          </xsd:element>
+        </xsd:schema>
+    """))
+
+    element = schema.get_element('ns0:container')
+    expected = load_xml("""
+        <ns0:container xmlns:ns0="http://tests.python-zeep.org/">
+          <ns0:item_1>foo</ns0:item_1>
+          <ns0:item_2>bar</ns0:item_2>
+          <ns0:item_3>foo</ns0:item_3>
+        </ns0:container>
+    """)
+    with pytest.raises(XMLParseError):
+        element.parse(expected, schema)
+
+
+def test_parse_check_mixed():
+    schema = xsd.Schema(load_xml("""
+        <?xml version="1.0"?>
+        <xsd:schema
+                xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+                xmlns:tns="http://tests.python-zeep.org/"
+                elementFormDefault="qualified"
+                targetNamespace="http://tests.python-zeep.org/">
+          <xsd:element name="container">
+            <xsd:complexType xmlns:xsd="http://www.w3.org/2001/XMLSchema">
+              <xsd:sequence>
+                <xsd:choice maxOccurs="unbounded">
+                  <xsd:element name="item_1" type="xsd:string"/>
+                  <xsd:element name="item_2" type="xsd:string"/>
+                </xsd:choice>
+                <xsd:element name="item_3" type="xsd:string"/>
+              </xsd:sequence>
+            </xsd:complexType>
+          </xsd:element>
+        </xsd:schema>
+    """))
+
+    element = schema.get_element('ns0:container')
+    expected = load_xml("""
+        <ns0:container xmlns:ns0="http://tests.python-zeep.org/">
+          <ns0:item_1>foo</ns0:item_1>
+          <ns0:item_2>bar</ns0:item_2>
+          <ns0:item_3>foo</ns0:item_3>
+        </ns0:container>
+    """)
+    element.parse(expected, schema)
+
+
+def test_parse_check_mixed_choices():
+    schema = xsd.Schema(load_xml("""
+        <?xml version="1.0"?>
+        <schema
+                xmlns="http://www.w3.org/2001/XMLSchema"
+                xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+                xmlns:tns="http://tests.python-zeep.org/"
+                elementFormDefault="qualified"
+                targetNamespace="http://tests.python-zeep.org/">
+          <element name="container">
+            <complexType>
+              <sequence>
+                <choice>
+                  <choice>
+                    <element name="item_1_1" type="string"/>
+                    <sequence>
+                      <element name="item_1_2a" type="string"/>
+                      <element name="item_1_2b" type="string" minOccurs="0"/>
+                    </sequence>
+                  </choice>
+                  <element name="item_2" type="string"/>
+                  <element name="item_3" type="string"/>
+                </choice>
+                <element name="isRegistered" type="boolean" fixed="true" minOccurs="0"/>
+              </sequence>
+            </complexType>
+          </element>
+        </schema>
+    """))
+
+    element = schema.get_element('ns0:container')
+
+    # item_1_1
+    value = element(item_1_1="foo")
+    assert value.item_1_1 == 'foo'
+
+    node = etree.Element('document')
+    element.render(node, value)
+
+    expected = """
+      <document>
+        <ns0:container xmlns:ns0="http://tests.python-zeep.org/">
+          <ns0:item_1_1>foo</ns0:item_1_1>
+        </ns0:container>
+      </document>
+    """
+    assert_nodes_equal(expected, node)
+
+    # item_1_2a
+    value = element(item_1_2a="foo")
+    node = etree.Element('document')
+    element.render(node, value)
+
+    expected = """
+      <document>
+        <ns0:container xmlns:ns0="http://tests.python-zeep.org/">
+          <ns0:item_1_2a>foo</ns0:item_1_2a>
+        </ns0:container>
+      </document>
+    """
+    assert_nodes_equal(expected, node)
+
+    # item_1_2a & item_1_2b
+    value = element(item_1_2a="foo", item_1_2b="bar")
+    node = etree.Element('document')
+    element.render(node, value)
+
+    expected = """
+      <document>
+        <ns0:container xmlns:ns0="http://tests.python-zeep.org/">
+          <ns0:item_1_2a>foo</ns0:item_1_2a>
+          <ns0:item_1_2b>bar</ns0:item_1_2b>
+        </ns0:container>
+      </document>
+    """
+    assert_nodes_equal(expected, node)
+
+    # item_2
+    value = element(item_2="foo")
+    assert value.item_2 == 'foo'
+    node = etree.Element('document')
+    element.render(node, value)
+
+    expected = """
+      <document>
+        <ns0:container xmlns:ns0="http://tests.python-zeep.org/">
+          <ns0:item_2>foo</ns0:item_2>
+        </ns0:container>
+      </document>
+    """
+    assert_nodes_equal(expected, node)
+
+    # item_3
+    value = element(item_3="foo")
+    assert value.item_3 == 'foo'
+    node = etree.Element('document')
+    element.render(node, value)
+
+    expected = """
+      <document>
+        <ns0:container xmlns:ns0="http://tests.python-zeep.org/">
+          <ns0:item_3>foo</ns0:item_3>
+        </ns0:container>
+      </document>
+    """
+    assert_nodes_equal(expected, node)
diff --git a/tests/test_xsd_complex_types.py b/tests/test_xsd_complex_types.py
new file mode 100644
index 0000000..cdfbde3
--- /dev/null
+++ b/tests/test_xsd_complex_types.py
@@ -0,0 +1,233 @@
+import pytest
+
+from lxml import etree
+from tests.utils import assert_nodes_equal, load_xml, render_node
+from zeep import xsd
+
+
+def test_single_node():
+    schema = xsd.Schema(load_xml("""
+        <?xml version="1.0"?>
+        <schema xmlns="http://www.w3.org/2001/XMLSchema"
+                xmlns:tns="http://tests.python-zeep.org/"
+                targetNamespace="http://tests.python-zeep.org/"
+                elementFormDefault="qualified">
+          <element name="container">
+            <complexType>
+              <sequence>
+                <element minOccurs="0" maxOccurs="1" name="item" type="string" />
+              </sequence>
+            </complexType>
+          </element>
+        </schema>
+    """))
+    schema.set_ns_prefix('tns', 'http://tests.python-zeep.org/')
+
+    container_elm = schema.get_element('tns:container')
+    obj = container_elm(item='bar')
+
+    expected = """
+      <document>
+        <ns0:container xmlns:ns0="http://tests.python-zeep.org/">
+          <ns0:item>bar</ns0:item>
+        </ns0:container>
+      </document>
+    """
+    result = render_node(container_elm, obj)
+    assert_nodes_equal(result, expected)
+
+    obj = container_elm.parse(result[0], schema)
+    assert obj.item == 'bar'
+
+
+def test_nested_sequence():
+    schema = xsd.Schema(load_xml("""
+        <?xml version="1.0"?>
+        <schema xmlns="http://www.w3.org/2001/XMLSchema"
+                xmlns:tns="http://tests.python-zeep.org/"
+                targetNamespace="http://tests.python-zeep.org/"
+                elementFormDefault="qualified">
+          <element name="container">
+            <complexType>
+              <sequence>
+                <element minOccurs="0" maxOccurs="1" name="item">
+                  <complexType>
+                    <sequence>
+                      <element name="x" type="integer"/>
+                      <element name="y" type="integer"/>
+                    </sequence>
+                  </complexType>
+                </element>
+              </sequence>
+            </complexType>
+          </element>
+        </schema>
+    """))
+    schema.set_ns_prefix('tns', 'http://tests.python-zeep.org/')
+
+    container_elm = schema.get_element('tns:container')
+    obj = container_elm(item={'x': 1, 'y': 2})
+
+    expected = """
+      <document>
+        <ns0:container xmlns:ns0="http://tests.python-zeep.org/">
+          <ns0:item>
+            <ns0:x>1</ns0:x>
+            <ns0:y>2</ns0:y>
+          </ns0:item>
+        </ns0:container>
+      </document>
+    """
+    result = render_node(container_elm, obj)
+    assert_nodes_equal(result, expected)
+
+    obj = container_elm.parse(result[0], schema)
+    assert obj.item.x == 1
+    assert obj.item.y == 2
+
+
+def test_single_node_array():
+    schema = xsd.Schema(load_xml("""
+        <?xml version="1.0"?>
+        <schema xmlns="http://www.w3.org/2001/XMLSchema"
+                xmlns:tns="http://tests.python-zeep.org/"
+                targetNamespace="http://tests.python-zeep.org/"
+                elementFormDefault="qualified">
+          <element name="container">
+            <complexType>
+              <sequence>
+                <element minOccurs="0" maxOccurs="unbounded" name="item" type="string" />
+              </sequence>
+            </complexType>
+          </element>
+        </schema>
+    """))
+    schema.set_ns_prefix('tns', 'http://tests.python-zeep.org/')
+
+    container_elm = schema.get_element('tns:container')
+    obj = container_elm(item=['item-1', 'item-2', 'item-3'])
+    assert obj.item == ['item-1', 'item-2', 'item-3']
+
+    expected = """
+      <document>
+        <ns0:container xmlns:ns0="http://tests.python-zeep.org/">
+          <ns0:item>item-1</ns0:item>
+          <ns0:item>item-2</ns0:item>
+          <ns0:item>item-3</ns0:item>
+        </ns0:container>
+      </document>
+    """
+    result = render_node(container_elm, obj)
+    assert_nodes_equal(result, expected)
+
+    obj = container_elm.parse(result[0], schema)
+    assert obj.item == ['item-1', 'item-2', 'item-3']
+
+
+def test_single_node_no_iterable():
+    schema = xsd.Schema(load_xml("""
+        <?xml version="1.0"?>
+        <schema xmlns="http://www.w3.org/2001/XMLSchema"
+                xmlns:tns="http://tests.python-zeep.org/"
+                targetNamespace="http://tests.python-zeep.org/"
+                elementFormDefault="qualified">
+          <element name="container">
+            <complexType>
+              <sequence>
+                <element minOccurs="0" maxOccurs="1" name="item" type="string" />
+              </sequence>
+            </complexType>
+          </element>
+        </schema>
+    """))
+    schema.set_ns_prefix('tns', 'http://tests.python-zeep.org/')
+
+    container_elm = schema.get_element('tns:container')
+
+    obj = container_elm(item=['item-1', 'item-2', 'item-3'])
+    assert obj.item == ['item-1', 'item-2', 'item-3']
+
+    with pytest.raises(ValueError):
+        render_node(container_elm, obj)
+
+
+def test_complex_any_types():
+    # see https://github.com/mvantellingen/python-zeep/issues/252
+    schema = xsd.Schema(load_xml("""
+        <?xml version="1.0"?>
+        <schema xmlns="http://www.w3.org/2001/XMLSchema"
+                xmlns:tns="http://tests.python-zeep.org/"
+                targetNamespace="http://tests.python-zeep.org/"
+                elementFormDefault="qualified">
+          <element name="container">
+            <complexType>
+              <sequence>
+                <element minOccurs="0" maxOccurs="1" name="auth" type="anyType" />
+                <element minOccurs="0" maxOccurs="1" name="params" type="anyType" />
+              </sequence>
+            </complexType>
+          </element>
+        </schema>
+    """))
+
+    schema.set_ns_prefix('tns', 'http://tests.python-zeep.org/')
+    KeyValueData = xsd.Element(
+        '{http://xml.apache.org/xml-soap}KeyValueData',
+        xsd.ComplexType(
+            xsd.Sequence([
+                xsd.Element(
+                    'key',
+                    xsd.AnyType(),
+                ),
+                xsd.Element(
+                    'value',
+                    xsd.AnyType(),
+                ),
+            ]),
+        ),
+    )
+
+    Map = xsd.ComplexType(
+        xsd.Sequence([
+            xsd.Element(
+                'item',
+                xsd.AnyType(),
+                min_occurs=1,
+                max_occurs="unbounded"),
+        ]),
+        qname=etree.QName('{http://xml.apache.org/xml-soap}Map'))
+
+    header_Username = KeyValueData(xsd.AnyObject(xsd.String(), 'Username'), value=xsd.AnyObject(xsd.String(), 'abc'))
+    header_ShopId = KeyValueData(xsd.AnyObject(xsd.String(), 'ShopId'), value=xsd.AnyObject(xsd.Int(), 123))
+    auth = Map(item=[header_Username, header_ShopId])
+
+    header_LimitNum = KeyValueData(xsd.AnyObject(xsd.String(), 'LimitNum'), value=xsd.AnyObject(xsd.Int(), 2))
+    params = Map(item=[header_LimitNum])
+
+    container = schema.get_element('ns0:container')
+    obj = container(auth=auth, params=params)
+
+    result = render_node(container, obj)
+    expected = load_xml("""
+    <document>
+      <ns0:container xmlns:ns0="http://tests.python-zeep.org/">
+        <ns0:auth xmlns:ns1="http://xml.apache.org/xml-soap" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="ns1:Map">
+          <item>
+            <key xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="xs:string">Username</key>
+            <value xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="xs:string">abc</value>
+          </item>
+          <item>
+            <key xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="xs:string">ShopId</key>
+            <value xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="xs:int">123</value>
+          </item>
+        </ns0:auth>
+        <ns0:params xmlns:ns2="http://xml.apache.org/xml-soap" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="ns2:Map">
+          <item>
+            <key xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="xs:string">LimitNum</key>
+            <value xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="xs:int">2</value>
+          </item>
+        </ns0:params>
+      </ns0:container>
+    </document>
+    """)  # noqa
+    assert_nodes_equal(result, expected)
diff --git a/tests/test_xsd_extension.py b/tests/test_xsd_extension.py
new file mode 100644
index 0000000..d8c5a8f
--- /dev/null
+++ b/tests/test_xsd_extension.py
@@ -0,0 +1,597 @@
+import datetime
+import io
+
+from lxml import etree
+
+from tests.utils import DummyTransport, assert_nodes_equal, load_xml, render_node
+from zeep import xsd
+
+
+def test_simple_content_extension():
+    schema = xsd.Schema(load_xml("""
+        <?xml version="1.0"?>
+        <xsd:schema
+            xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+            xmlns:tns="http://tests.python-zeep.org/"
+            targetNamespace="http://tests.python-zeep.org/"
+            elementFormDefault="qualified">
+
+          <xsd:element name="ShoeSize">
+            <xsd:complexType>
+              <xsd:simpleContent>
+                <xsd:extension base="xsd:integer">
+                  <xsd:attribute name="sizing" type="xsd:string" />
+                </xsd:extension>
+              </xsd:simpleContent>
+            </xsd:complexType>
+          </xsd:element>
+        </xsd:schema>
+    """.strip()))
+    shoe_type = schema.get_element('{http://tests.python-zeep.org/}ShoeSize')
+
+    obj = shoe_type(20, sizing='EUR')
+
+    node = render_node(shoe_type, obj)
+    expected = """
+        <document>
+            <ns0:ShoeSize
+                xmlns:ns0="http://tests.python-zeep.org/"
+                sizing="EUR">20</ns0:ShoeSize>
+        </document>
+    """
+    assert_nodes_equal(expected, node)
+
+    obj = shoe_type.parse(node[0], schema)
+    assert obj._value_1 == 20
+    assert obj.sizing == 'EUR'
+
+
+def test_complex_content_sequence_extension():
+    schema = xsd.Schema(load_xml("""
+        <?xml version="1.0"?>
+        <xsd:schema
+            xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+            xmlns:tns="http://tests.python-zeep.org/"
+            targetNamespace="http://tests.python-zeep.org/"
+            elementFormDefault="qualified">
+
+            <xsd:complexType name="Address">
+              <xsd:complexContent>
+                <xsd:extension base="tns:Name">
+                  <xsd:sequence>
+                    <xsd:element name="country" type="xsd:string"/>
+                  </xsd:sequence>
+                </xsd:extension>
+              </xsd:complexContent>
+            </xsd:complexType>
+          <xsd:element name="Address" type="tns:Address"/>
+
+          <xsd:complexType name="Name">
+            <xsd:sequence>
+              <xsd:element name="first_name" type="xsd:string"/>
+              <xsd:element name="last_name" type="xsd:string"/>
+            </xsd:sequence>
+          </xsd:complexType>
+        </xsd:schema>
+    """))
+    address_type = schema.get_element('{http://tests.python-zeep.org/}Address')
+
+    obj = address_type(
+        first_name='foo', last_name='bar', country='The Netherlands')
+
+    node = etree.Element('document')
+    address_type.render(node, obj)
+    expected = """
+        <document>
+            <ns0:Address xmlns:ns0="http://tests.python-zeep.org/">
+                <ns0:first_name>foo</ns0:first_name>
+                <ns0:last_name>bar</ns0:last_name>
+                <ns0:country>The Netherlands</ns0:country>
+            </ns0:Address>
+        </document>
+    """
+    assert_nodes_equal(expected, node)
+
+
+def test_complex_content_with_recursive_elements():
+    schema = xsd.Schema(load_xml("""
+        <?xml version="1.0"?>
+        <xsd:schema
+            xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+            xmlns:tns="http://tests.python-zeep.org/"
+            targetNamespace="http://tests.python-zeep.org/"
+            elementFormDefault="qualified">
+
+            <xsd:complexType name="Pet">
+              <xsd:complexContent>
+                <xsd:extension base="tns:Name">
+                  <xsd:sequence>
+                    <xsd:element name="children" type="tns:Pet"/>
+                  </xsd:sequence>
+                </xsd:extension>
+              </xsd:complexContent>
+            </xsd:complexType>
+          <xsd:element name="Pet" type="tns:Pet"/>
+
+          <xsd:complexType name="Name">
+            <xsd:sequence>
+              <xsd:element name="name" type="xsd:string"/>
+              <xsd:element name="common_name" type="xsd:string"/>
+            </xsd:sequence>
+          </xsd:complexType>
+        </xsd:schema>
+    """))
+    pet_type = schema.get_element('{http://tests.python-zeep.org/}Pet')
+    assert(pet_type.signature() == 'name: xsd:string, common_name: xsd:string, children: Pet')
+
+    obj = pet_type(
+        name='foo', common_name='bar')
+
+    node = etree.Element('document')
+    pet_type.render(node, obj)
+    expected = """
+        <document>
+            <ns0:Pet xmlns:ns0="http://tests.python-zeep.org/">
+                <ns0:name>foo</ns0:name>
+                <ns0:common_name>bar</ns0:common_name>
+                <ns0:children/>
+            </ns0:Pet>
+        </document>
+    """
+    assert_nodes_equal(expected, node)
+
+
+def test_complex_content_sequence_extension_2():
+    schema = xsd.Schema(load_xml("""
+        <?xml version="1.0"?>
+        <xsd:schema
+            xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+            xmlns:tns="http://tests.python-zeep.org/"
+            targetNamespace="http://tests.python-zeep.org/"
+            elementFormDefault="qualified">
+
+          <xsd:element name="container" type="tns:baseResponse"/>
+          <xsd:complexType name="baseResponse">
+            <xsd:sequence>
+              <xsd:element name="item-1" type="xsd:string"/>
+              <xsd:element name="item-2" type="xsd:string"/>
+            </xsd:sequence>
+          </xsd:complexType>
+
+          <xsd:complexType name="response">
+            <xsd:complexContent>
+              <xsd:extension base="tns:baseResponse">
+                <xsd:sequence>
+                  <xsd:element name="item-3" type="xsd:string"/>
+                </xsd:sequence>
+              </xsd:extension>
+            </xsd:complexContent>
+          </xsd:complexType>
+        </xsd:schema>
+    """))
+    elm_cls = schema.get_element('{http://tests.python-zeep.org/}container')
+
+    node = load_xml("""
+        <ns0:container xmlns:ns0="http://tests.python-zeep.org/"
+                       xmlns:i="http://www.w3.org/2001/XMLSchema-instance"
+                       i:type="ns0:response">
+            <ns0:item-1>item-1</ns0:item-1>
+            <ns0:item-2>item-2</ns0:item-2>
+            <ns0:item-3>item-3</ns0:item-3>
+        </ns0:container>
+    """)
+    data = elm_cls.parse(node, schema)
+    assert data['item-1'] == 'item-1'
+    assert data['item-2'] == 'item-2'
+    assert data['item-3'] == 'item-3'
+
+
+def test_complex_type_with_extension_optional():
+    schema = xsd.Schema(load_xml("""
+        <?xml version="1.0"?>
+        <xsd:schema
+            xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+            xmlns:tns="http://tests.python-zeep.org/"
+            targetNamespace="http://tests.python-zeep.org/"
+            elementFormDefault="qualified">
+
+          <xsd:complexType name="containerType">
+            <xsd:complexContent>
+              <xsd:extension base="tns:base">
+                <xsd:sequence>
+                  <xsd:element name="main_1" type="xsd:string"/>
+                </xsd:sequence>
+              </xsd:extension>
+            </xsd:complexContent>
+          </xsd:complexType>
+          <xsd:element name="container" type="tns:containerType"/>
+
+          <xsd:complexType name="base">
+            <xsd:sequence>
+              <xsd:element minOccurs="0" name="base_1" type="tns:baseType"/>
+              <xsd:element minOccurs="0" name="base_2" type="xsd:string"/>
+            </xsd:sequence>
+          </xsd:complexType>
+
+          <xsd:complexType name="baseType">
+            <xsd:sequence>
+              <xsd:element minOccurs="0" name="base_1_1" type="xsd:string"/>
+            </xsd:sequence>
+          </xsd:complexType>
+        </xsd:schema>
+    """))
+    container_elm = schema.get_element('{http://tests.python-zeep.org/}container')
+    obj = container_elm(main_1='foo')
+
+    node = etree.Element('document')
+    container_elm.render(node, obj)
+    expected = """
+        <document>
+            <ns0:container xmlns:ns0="http://tests.python-zeep.org/">
+                <ns0:main_1>foo</ns0:main_1>
+            </ns0:container>
+        </document>
+    """
+    assert_nodes_equal(expected, node)
+
+    assert_nodes_equal(expected, node)
+    item = container_elm.parse(node.getchildren()[0], schema)
+    assert item.main_1 == 'foo'
+
+
+def test_complex_with_simple():
+    schema = xsd.Schema(load_xml("""
+        <?xml version="1.0"?>
+        <xsd:schema
+                xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+                xmlns:tns="http://tests.python-zeep.org/"
+                elementFormDefault="qualified"
+                targetNamespace="http://tests.python-zeep.org/">
+          <xsd:element name="Address">
+            <xsd:complexType>
+              <xsd:simpleContent>
+                <xsd:extension base="tns:DateTimeType">
+                  <xsd:attribute name="name" type="xsd:token"/>
+                </xsd:extension>
+              </xsd:simpleContent>
+            </xsd:complexType>
+          </xsd:element>
+
+          <xsd:simpleType name="DateTimeType">
+            <xsd:restriction base="xsd:dateTime"/>
+          </xsd:simpleType>
+        </xsd:schema>
+    """))
+    address_type = schema.get_element('ns0:Address')
+
+    assert address_type.type.signature()
+    val = datetime.datetime(2016, 5, 29, 11, 13, 45)
+    obj = address_type(val, name='foobie')
+
+    expected = """
+      <document>
+        <ns0:Address xmlns:ns0="http://tests.python-zeep.org/"
+            name="foobie">2016-05-29T11:13:45</ns0:Address>
+      </document>
+    """
+    node = etree.Element('document')
+    address_type.render(node, obj)
+    assert_nodes_equal(expected, node)
+
+
+def test_sequence_with_type():
+    schema = xsd.Schema(load_xml("""
+        <?xml version="1.0"?>
+        <schema
+                xmlns="http://www.w3.org/2001/XMLSchema"
+                xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+                xmlns:tns="http://tests.python-zeep.org/"
+                elementFormDefault="qualified"
+                targetNamespace="http://tests.python-zeep.org/">
+
+          <complexType name="base" abstract="true">
+            <sequence>
+              <element name="name" type="xsd:string" minOccurs="0"/>
+            </sequence>
+          </complexType>
+
+          <complexType name="subtype">
+            <complexContent>
+              <extension base="tns:base">
+                <attribute name="attr_1" type="xsd:string"/>
+              </extension>
+            </complexContent>
+          </complexType>
+
+          <complexType name="polytype">
+            <sequence>
+              <element name="item" type="tns:base" maxOccurs="unbounded" minOccurs="0"/>
+            </sequence>
+          </complexType>
+
+          <element name="Seq" type="tns:polytype"/>
+        </schema>
+    """))
+    seq = schema.get_type('ns0:polytype')
+    sub_type = schema.get_type('ns0:subtype')
+    value = seq(item=[sub_type(attr_1='test', name='name')])
+
+    node = etree.Element('document')
+    seq.render(node, value)
+
+    expected = """
+      <document>
+        <ns0:item
+            xmlns:ns0="http://tests.python-zeep.org/"
+            xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+            attr_1="test" xsi:type="ns0:subtype">
+          <ns0:name>name</ns0:name>
+        </ns0:item>
+      </document>
+    """
+    assert_nodes_equal(expected, node)
+
+
+def test_complex_simple_content():
+    schema = xsd.Schema(load_xml("""
+        <?xml version="1.0"?>
+        <xsd:schema
+            xmlns:tns="http://tests.python-zeep.org/"
+            xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+            targetNamespace="http://tests.python-zeep.org/"
+            elementFormDefault="qualified">
+          <xsd:element name="value" type="tns:UUID"/>
+
+          <xsd:complexType name="UUID">
+            <xsd:simpleContent>
+              <xsd:extension base="tns:UUID.Content">
+                <xsd:attribute name="schemeID">
+                  <xsd:simpleType>
+                    <xsd:restriction base="xsd:token">
+                      <xsd:maxLength value="60"/>
+                      <xsd:minLength value="1"/>
+                    </xsd:restriction>
+                  </xsd:simpleType>
+                </xsd:attribute>
+                <xsd:attribute name="schemeAgencyID">
+                  <xsd:simpleType>
+                    <xsd:restriction base="xsd:token">
+                      <xsd:maxLength value="60"/>
+                      <xsd:minLength value="1"/>
+                    </xsd:restriction>
+                  </xsd:simpleType>
+                </xsd:attribute>
+              </xsd:extension>
+            </xsd:simpleContent>
+          </xsd:complexType>
+          <xsd:simpleType name="UUID.Content">
+            <xsd:restriction base="xsd:token">
+              <xsd:maxLength value="36"/>
+              <xsd:minLength value="36"/>
+              <xsd:pattern value="[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}"/>
+            </xsd:restriction>
+          </xsd:simpleType>
+        </xsd:schema>
+    """))  # noqa
+    value_elm = schema.get_element('ns0:value')
+    value = value_elm('00163e0c-0ea1-1ed6-93af-e818529bc1f1')
+
+    node = etree.Element('document')
+    value_elm.render(node, value)
+    expected = """
+      <document>
+        <ns0:value xmlns:ns0="http://tests.python-zeep.org/">00163e0c-0ea1-1ed6-93af-e818529bc1f1</ns0:value>
+      </document>
+    """  # noqa
+    assert_nodes_equal(expected, node)
+
+    item = value_elm.parse(node.getchildren()[0], schema)
+    assert item._value_1 == '00163e0c-0ea1-1ed6-93af-e818529bc1f1'
+
+
+def test_issue_221():
+    transport = DummyTransport()
+    transport.bind(
+        'https://www.w3.org/TR/xmldsig-core/xmldsig-core-schema.xsd',
+        load_xml(io.open('tests/wsdl_files/xmldsig-core-schema.xsd', 'r').read().encode('utf-8')))
+
+    schema = xsd.Schema(load_xml("""
+        <?xml version="1.0"?>
+        <schema xmlns="http://www.w3.org/2001/XMLSchema"
+                xmlns:tns="http://tests.python-zeep.org/"
+                xmlns:ds="http://www.w3.org/2000/09/xmldsig#"
+                targetNamespace="http://tests.python-zeep.org/"
+                elementFormDefault="qualified">
+          <import namespace="http://www.w3.org/2000/09/xmldsig#"
+                  schemaLocation="https://www.w3.org/TR/xmldsig-core/xmldsig-core-schema.xsd"/>
+          <complexType name="BaseType">
+            <sequence>
+              <element ref="ds:Signature" minOccurs="0"/>
+            </sequence>
+            <attribute name="Id"/>
+          </complexType>
+          <element name="exportOrgRegistryRequest">
+            <complexType>
+              <complexContent>
+                <extension base="tns:BaseType">
+                  <sequence>
+                    <element name="SearchCriteria" maxOccurs="100">
+                      <complexType>
+                        <sequence>
+                          <choice>
+                            <choice>
+                              <element ref="tns:OGRNIP"/>
+                              <sequence>
+                                <element name="OGRN" type="string"/>
+                                <element name="KPP" type="string" minOccurs="0"/>
+                              </sequence>
+                            </choice>
+                            <element ref="tns:orgVersionGUID"/>
+                            <element ref="tns:orgRootEntityGUID"/>
+                          </choice>
+                          <element name="isRegistered" type="boolean" fixed="true" minOccurs="0">
+                          </element>
+                        </sequence>
+                      </complexType>
+                    </element>
+                    <element name="lastEditingDateFrom" type="date" minOccurs="0"/>
+                  </sequence>
+                </extension>
+              </complexContent>
+            </complexType>
+          </element>
+          <simpleType name="OGRNIPType">
+            <restriction base="string">
+              <length value="13"/>
+            </restriction>
+          </simpleType>
+          <element name="OGRNIP" type="tns:OGRNIPType"/>
+          <element name="orgVersionGUID" type="tns:GUIDType"/>
+          <element name="orgRootEntityGUID" type="tns:GUIDType"/>
+          <simpleType name="GUIDType">
+            <restriction base="string">
+              <pattern value="([0-9a-fA-F]){8}-([0-9a-fA-F]){4}-([0-9a-fA-F]){4}-([0-9a-fA-F]){4}-([0-9a-fA-F]){12}"/>
+            </restriction>
+          </simpleType>x
+        </schema>
+    """), transport=transport)
+
+    schema.set_ns_prefix('tns', 'http://tests.python-zeep.org/')
+    elm = schema.get_element('tns:exportOrgRegistryRequest')
+
+    # Args
+    obj = elm(None, {'OGRN': '123123123123', 'isRegistered': True})
+    node = etree.Element('document')
+    elm.render(node, obj)
+    expected = """
+      <document>
+        <ns0:exportOrgRegistryRequest xmlns:ns0="http://tests.python-zeep.org/">
+          <ns0:SearchCriteria>
+            <ns0:OGRN>123123123123</ns0:OGRN>
+            <ns0:isRegistered>true</ns0:isRegistered>
+          </ns0:SearchCriteria>
+        </ns0:exportOrgRegistryRequest>
+      </document>
+    """
+    assert_nodes_equal(expected, node)
+
+    obj = elm(SearchCriteria={'orgVersionGUID': '1234', 'isRegistered': False})
+    node = etree.Element('document')
+    elm.render(node, obj)
+    expected = """
+      <document>
+        <ns0:exportOrgRegistryRequest xmlns:ns0="http://tests.python-zeep.org/">
+          <ns0:SearchCriteria>
+            <ns0:orgVersionGUID>1234</ns0:orgVersionGUID>
+            <ns0:isRegistered>false</ns0:isRegistered>
+          </ns0:SearchCriteria>
+        </ns0:exportOrgRegistryRequest>
+      </document>
+    """
+    assert_nodes_equal(expected, node)
+
+    obj = elm(SearchCriteria={'OGRNIP': '123123123123', 'isRegistered': True})
+    node = etree.Element('document')
+    elm.render(node, obj)
+    expected = """
+      <document>
+        <ns0:exportOrgRegistryRequest xmlns:ns0="http://tests.python-zeep.org/">
+          <ns0:SearchCriteria>
+            <ns0:OGRNIP>123123123123</ns0:OGRNIP>
+            <ns0:isRegistered>true</ns0:isRegistered>
+          </ns0:SearchCriteria>
+        </ns0:exportOrgRegistryRequest>
+      </document>
+    """
+    assert_nodes_equal(expected, node)
+
+
+def test_complex_content_extension_with_sequence():
+    schema = xsd.Schema(load_xml("""
+        <?xml version="1.0"?>
+        <xsd:schema
+            xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+            xmlns:tns="http://tests.python-zeep.org/"
+            targetNamespace="http://tests.python-zeep.org/"
+            elementFormDefault="qualified">
+
+            <xsd:complexType name="Package">
+              <xsd:complexContent>
+                <xsd:extension base="tns:AbstractPackage">
+                  <xsd:attribute name="id" type="xsd:string"/>
+                </xsd:extension>
+              </xsd:complexContent>
+            </xsd:complexType>
+
+            <xsd:complexType name="SpecialPackage">
+              <xsd:complexContent>
+                <xsd:extension base="tns:Package">
+                    <xsd:sequence>
+                        <xsd:element name="otherElement" type="xsd:string"/>
+                    </xsd:sequence>
+                </xsd:extension>
+              </xsd:complexContent>
+            </xsd:complexType>
+
+            <xsd:complexType name="AbstractPackage">
+              <xsd:attribute name="pkg_id" type="xsd:string"/>
+            </xsd:complexType>
+
+          <xsd:element name="SpecialPackage" type="tns:SpecialPackage"/>
+        </xsd:schema>
+    """))
+    address_type = schema.get_element('{http://tests.python-zeep.org/}SpecialPackage')
+
+    obj = address_type(
+        id='testString', pkg_id='nameId')
+
+    node = etree.Element('document')
+    address_type.render(node, obj)
+    expected = """
+        <document>
+            <ns0:SpecialPackage xmlns:ns0="http://tests.python-zeep.org/" pkg_id="nameId" id="testString">
+                <ns0:otherElement/>
+            </ns0:SpecialPackage>
+        </document>
+    """
+    assert_nodes_equal(expected, node)
+
+
+def test_extension_abstract_complex_type():
+    schema = xsd.Schema(load_xml("""
+        <?xml version="1.0"?>
+        <xsd:schema
+            xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+            xmlns:tns="http://tests.python-zeep.org/"
+            targetNamespace="http://tests.python-zeep.org/"
+            elementFormDefault="qualified">
+
+            <xsd:complexType name="Package" abstract="true"/>
+
+            <xsd:complexType name="SpecialPackage">
+              <xsd:complexContent>
+                <xsd:extension base="tns:Package">
+                    <xsd:sequence>
+                        <xsd:element name="item" type="xsd:string"/>
+                    </xsd:sequence>
+                </xsd:extension>
+              </xsd:complexContent>
+            </xsd:complexType>
+
+          <xsd:element name="SpecialPackage" type="tns:SpecialPackage"/>
+        </xsd:schema>
+    """))
+    package_cls = schema.get_element('{http://tests.python-zeep.org/}SpecialPackage')
+
+    obj = package_cls(item='foo')
+
+    node = etree.Element('document')
+    package_cls.render(node, obj)
+    expected = """
+        <document>
+            <ns0:SpecialPackage xmlns:ns0="http://tests.python-zeep.org/">
+                <ns0:item>foo</ns0:item>
+            </ns0:SpecialPackage>
+        </document>
+    """
+    assert_nodes_equal(expected, node)
diff --git a/tests/test_xsd_integration.py b/tests/test_xsd_integration.py
index 06df987..64d9847 100644
--- a/tests/test_xsd_integration.py
+++ b/tests/test_xsd_integration.py
@@ -1,3 +1,5 @@
+import copy
+
 import pytest
 from lxml import etree
 
@@ -125,7 +127,6 @@ def test_array():
     """
     node = etree.Element('document', nsmap=schema._prefix_map_custom)
     address_type.render(node, obj)
-    print(etree.tostring(node))
     assert_nodes_equal(expected, node)
 
 
@@ -902,3 +903,51 @@ def test_empty_xmlns():
     """)
     item = container_elm.parse(node, schema)
     assert item._value_1 == 'foo'
+
+
+def test_keep_objects_intact():
+    node = etree.fromstring("""
+        <?xml version="1.0"?>
+        <xsd:schema
+                xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+                xmlns:tns="http://tests.python-zeep.org/"
+                attributeFormDefault="qualified"
+                elementFormDefault="qualified"
+                targetNamespace="http://tests.python-zeep.org/">
+          <xsd:element name="Address">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="name" type="xsd:string"/>
+                <xsd:element minOccurs="0" name="optional" type="xsd:string"/>
+                <xsd:element name="container" nillable="true" type="tns:Container"/>
+              </xsd:sequence>
+            </xsd:complexType>
+          </xsd:element>
+
+          <xsd:complexType name="Container">
+            <xsd:sequence>
+              <xsd:element maxOccurs="unbounded" minOccurs="0" name="service"
+                           nillable="true" type="tns:ServiceRequestType"/>
+            </xsd:sequence>
+          </xsd:complexType>
+
+          <xsd:complexType name="ServiceRequestType">
+            <xsd:sequence>
+              <xsd:element name="name" type="xsd:string"/>
+            </xsd:sequence>
+          </xsd:complexType>
+        </xsd:schema>
+    """.strip())
+
+    schema = xsd.Schema(node)
+    address_type = schema.get_element('{http://tests.python-zeep.org/}Address')
+    obj = address_type(name='foo', container={'service': [{'name': 'foo'}]})
+
+    org_obj = copy.deepcopy(obj)
+
+    node = etree.Element('document')
+    address_type.render(node, obj)
+
+    print(org_obj)
+    print(obj)
+    assert org_obj['container']['service'] == obj['container']['service']
diff --git a/tests/test_xsd_parse.py b/tests/test_xsd_parse.py
index c7f959e..3357d7c 100644
--- a/tests/test_xsd_parse.py
+++ b/tests/test_xsd_parse.py
@@ -30,6 +30,33 @@ def test_sequence_parse_basic():
     assert obj.item_2 == 'bar'
 
 
+def test_sequence_parse_max_occurs_infinite_loop():
+    custom_type = xsd.Element(
+        etree.QName('http://tests.python-zeep.org/', 'authentication'),
+        xsd.ComplexType(
+            xsd.Sequence([
+                xsd.Element(
+                    etree.QName('http://tests.python-zeep.org/', 'item_1'),
+                    xsd.String()),
+                xsd.Element(
+                    etree.QName('http://tests.python-zeep.org/', 'item_2'),
+                    xsd.String()),
+            ], max_occurs='unbounded')
+        ))
+    expected = etree.fromstring("""
+        <ns0:container xmlns:ns0="http://tests.python-zeep.org/">
+          <ns0:item_1>foo</ns0:item_1>
+          <ns0:item_2>bar</ns0:item_2>
+        </ns0:container>
+    """)
+    obj = custom_type.parse(expected, None)
+    assert obj._value_1 == [
+        {
+            'item_1': 'foo',
+            'item_2': 'bar',
+        }
+    ]
+
 def test_sequence_parse_basic_with_attrs():
     custom_element = xsd.Element(
         etree.QName('http://tests.python-zeep.org/', 'authentication'),
diff --git a/tests/test_xsd_schemas.py b/tests/test_xsd_schemas.py
new file mode 100644
index 0000000..269c3fe
--- /dev/null
+++ b/tests/test_xsd_schemas.py
@@ -0,0 +1,656 @@
+import pytest
+from lxml import etree
+
+from tests.utils import DummyTransport, load_xml
+from zeep import exceptions, xsd
+from zeep.xsd.builtins import Schema as Schema
+from zeep.exceptions import ZeepWarning
+
+
+def test_default_types():
+    schema = xsd.Schema()
+    xsd_string = schema.get_type('{http://www.w3.org/2001/XMLSchema}string')
+    assert xsd_string == xsd.String()
+
+
+def test_default_types_not_found():
+    schema = xsd.Schema()
+    with pytest.raises(exceptions.LookupError):
+        schema.get_type('{http://www.w3.org/2001/XMLSchema}bar')
+
+
+def test_default_elements():
+    schema = xsd.Schema()
+    xsd_schema = schema.get_element('{http://www.w3.org/2001/XMLSchema}schema')
+    isinstance(xsd_schema, Schema)
+
+
+def test_default_elements_not_found():
+    schema = xsd.Schema()
+    with pytest.raises(exceptions.LookupError):
+        schema.get_element('{http://www.w3.org/2001/XMLSchema}bar')
+
+
+def test_invalid_namespace_handling():
+    schema = xsd.Schema()
+    qname = '{http://tests.python-zeep.org/404}foo'
+
+    with pytest.raises(exceptions.NamespaceError) as exc:
+        schema.get_element(qname)
+    assert qname in str(exc.value.message)
+
+    with pytest.raises(exceptions.NamespaceError) as exc:
+        schema.get_type(qname)
+    assert qname in str(exc.value.message)
+
+    with pytest.raises(exceptions.NamespaceError) as exc:
+        schema.get_group(qname)
+    assert qname in str(exc.value.message)
+
+    with pytest.raises(exceptions.NamespaceError) as exc:
+        schema.get_attribute(qname)
+    assert qname in str(exc.value.message)
+
+    with pytest.raises(exceptions.NamespaceError) as exc:
+        schema.get_attribute_group(qname)
+    assert qname in str(exc.value.message)
+
+
+def test_invalid_localname_handling():
+    schema = xsd.Schema(load_xml("""
+        <?xml version="1.0"?>
+        <xs:schema
+            xmlns:xs="http://www.w3.org/2001/XMLSchema"
+            xmlns:tns="http://tests.python-zeep.org/"
+            targetNamespace="http://tests.python-zeep.org/"
+            elementFormDefault="qualified">
+        </xs:schema>
+    """))
+
+    qname = '{http://tests.python-zeep.org/}foo'
+    namespace = 'http://tests.python-zeep.org/'
+    localname = 'foo'
+
+    with pytest.raises(exceptions.LookupError) as exc:
+        schema.get_element(qname)
+    assert namespace in str(exc.value.message)
+    assert localname in str(exc.value.message)
+
+    with pytest.raises(exceptions.LookupError) as exc:
+        schema.get_type(qname)
+    assert namespace in str(exc.value.message)
+    assert localname in str(exc.value.message)
+
+    with pytest.raises(exceptions.LookupError) as exc:
+        schema.get_group(qname)
+    assert namespace in str(exc.value.message)
+    assert localname in str(exc.value.message)
+
+    with pytest.raises(exceptions.LookupError) as exc:
+        schema.get_attribute(qname)
+    assert namespace in str(exc.value.message)
+    assert localname in str(exc.value.message)
+
+    with pytest.raises(exceptions.LookupError) as exc:
+        schema.get_attribute_group(qname)
+    assert namespace in str(exc.value.message)
+    assert localname in str(exc.value.message)
+
+
+def test_schema_repr_none():
+    schema = xsd.Schema()
+    assert repr(schema) == "<Schema(location='<none>')>"
+
+
+def test_schema_repr_val():
+    schema = xsd.Schema(load_xml("""
+        <?xml version="1.0"?>
+        <xs:schema
+            xmlns:xs="http://www.w3.org/2001/XMLSchema"
+            xmlns:tns="http://tests.python-zeep.org/"
+            targetNamespace="http://tests.python-zeep.org/"
+            elementFormDefault="qualified">
+        </xs:schema>
+    """))
+    assert repr(schema) == "<Schema(location=None)>"
+
+
+def test_schema_doc_repr_val():
+    schema = xsd.Schema(load_xml("""
+        <?xml version="1.0"?>
+        <xs:schema
+            xmlns:xs="http://www.w3.org/2001/XMLSchema"
+            xmlns:tns="http://tests.python-zeep.org/"
+            targetNamespace="http://tests.python-zeep.org/"
+            elementFormDefault="qualified">
+        </xs:schema>
+    """))
+    doc = schema._get_schema_document('http://tests.python-zeep.org/')
+    assert repr(doc) == "<SchemaDocument(location=None, tns='http://tests.python-zeep.org/', is_empty=True)>"
+
+
+def test_multiple_extension():
+    node_a = etree.fromstring("""
+        <?xml version="1.0"?>
+        <xs:schema
+            xmlns:xs="http://www.w3.org/2001/XMLSchema"
+            xmlns:tns="http://tests.python-zeep.org/a"
+            targetNamespace="http://tests.python-zeep.org/a"
+            xmlns:b="http://tests.python-zeep.org/b"
+            elementFormDefault="qualified">
+
+            <xs:import
+                schemaLocation="http://tests.python-zeep.org/b.xsd"
+                namespace="http://tests.python-zeep.org/b"/>
+
+            <xs:complexType name="type_a">
+              <xs:complexContent>
+                <xs:extension base="b:type_b"/>
+              </xs:complexContent>
+            </xs:complexType>
+            <xs:element name="typetje" type="tns:type_a"/>
+
+        </xs:schema>
+    """.strip())
+
+    node_b = etree.fromstring("""
+        <?xml version="1.0"?>
+        <xs:schema
+            xmlns:xs="http://www.w3.org/2001/XMLSchema"
+            xmlns:tns="http://tests.python-zeep.org/b"
+            targetNamespace="http://tests.python-zeep.org/b"
+            xmlns:c="http://tests.python-zeep.org/c"
+            elementFormDefault="qualified">
+
+            <xs:import
+                schemaLocation="http://tests.python-zeep.org/c.xsd"
+                namespace="http://tests.python-zeep.org/c"/>
+
+            <xs:complexType name="type_b">
+              <xs:complexContent>
+                <xs:extension base="c:type_c"/>
+              </xs:complexContent>
+            </xs:complexType>
+        </xs:schema>
+    """.strip())
+
+    node_c = etree.fromstring("""
+        <?xml version="1.0"?>
+        <xs:schema
+            xmlns:xs="http://www.w3.org/2001/XMLSchema"
+            xmlns:tns="http://tests.python-zeep.org/c"
+            targetNamespace="http://tests.python-zeep.org/c"
+            elementFormDefault="qualified">
+
+            <xs:complexType name="type_c">
+              <xs:complexContent>
+                <xs:extension base="tns:type_d"/>
+              </xs:complexContent>
+            </xs:complexType>
+
+            <xs:complexType name="type_d">
+                <xs:attribute name="wat" type="xs:string" />
+            </xs:complexType>
+        </xs:schema>
+    """.strip())
+    etree.XMLSchema(node_c)
+
+    transport = DummyTransport()
+    transport.bind('http://tests.python-zeep.org/b.xsd', node_b)
+    transport.bind('http://tests.python-zeep.org/c.xsd', node_c)
+
+    schema = xsd.Schema(node_a, transport=transport)
+    type_a = schema.get_type('ns0:type_a')
+    type_a(wat='x')
+
+
+def test_global_element_and_type():
+    node_a = etree.fromstring("""
+        <?xml version="1.0"?>
+        <xs:schema
+            xmlns:xs="http://www.w3.org/2001/XMLSchema"
+            xmlns:tns="http://tests.python-zeep.org/a"
+            targetNamespace="http://tests.python-zeep.org/a"
+            xmlns:b="http://tests.python-zeep.org/b"
+            elementFormDefault="qualified">
+
+            <xs:import
+                schemaLocation="http://tests.python-zeep.org/b.xsd"
+                namespace="http://tests.python-zeep.org/b"/>
+
+            <xs:complexType name="refs">
+              <xs:sequence>
+                <xs:element ref="b:ref_elm"/>
+              </xs:sequence>
+              <xs:attribute ref="b:ref_attr"/>
+            </xs:complexType>
+
+        </xs:schema>
+    """.strip())
+
+    node_b = etree.fromstring("""
+        <?xml version="1.0"?>
+        <xs:schema
+            xmlns:xs="http://www.w3.org/2001/XMLSchema"
+            xmlns:tns="http://tests.python-zeep.org/b"
+            targetNamespace="http://tests.python-zeep.org/b"
+            xmlns:c="http://tests.python-zeep.org/c"
+            elementFormDefault="qualified">
+
+            <xs:import
+                schemaLocation="http://tests.python-zeep.org/c.xsd"
+                namespace="http://tests.python-zeep.org/c"/>
+
+            <xs:element name="ref_elm" type="xs:string"/>
+            <xs:attribute name="ref_attr" type="xs:string"/>
+        </xs:schema>
+    """.strip())
+
+    node_c = etree.fromstring("""
+        <?xml version="1.0"?>
+        <xs:schema
+            xmlns:xs="http://www.w3.org/2001/XMLSchema"
+            xmlns:tns="http://tests.python-zeep.org/c"
+            targetNamespace="http://tests.python-zeep.org/c"
+            elementFormDefault="qualified">
+
+            <xs:complexType name="type_a">
+              <xs:sequence>
+                <xs:element name="item_a" type="xs:string"/>
+              </xs:sequence>
+            </xs:complexType>
+            <xs:element name="item" type="xs:string"/>
+        </xs:schema>
+    """.strip())
+    etree.XMLSchema(node_c)
+
+    transport = DummyTransport()
+    transport.bind('http://tests.python-zeep.org/b.xsd', node_b)
+    transport.bind('http://tests.python-zeep.org/c.xsd', node_c)
+
+    schema = xsd.Schema(node_a, transport=transport)
+    type_a = schema.get_type('{http://tests.python-zeep.org/c}type_a')
+
+    type_a = schema.get_type('{http://tests.python-zeep.org/c}type_a')
+    type_a(item_a='x')
+
+    elm = schema.get_element('{http://tests.python-zeep.org/c}item')
+    elm('x')
+
+    elm = schema.get_type('{http://tests.python-zeep.org/a}refs')
+    elm(ref_elm='foo', ref_attr='bar')
+
+
+def test_cyclic_imports():
+    schema_a = etree.fromstring("""
+        <?xml version="1.0"?>
+        <xs:schema
+            xmlns:xs="http://www.w3.org/2001/XMLSchema"
+            xmlns:tns="http://tests.python-zeep.org/a"
+            targetNamespace="http://tests.python-zeep.org/a"
+            xmlns:b="http://tests.python-zeep.org/b"
+            elementFormDefault="qualified">
+
+            <xs:import
+                schemaLocation="http://tests.python-zeep.org/b.xsd"
+                namespace="http://tests.python-zeep.org/b"/>
+        </xs:schema>
+    """.strip())
+
+    schema_b = etree.fromstring("""
+        <?xml version="1.0"?>
+        <xs:schema
+            xmlns:xs="http://www.w3.org/2001/XMLSchema"
+            xmlns:tns="http://tests.python-zeep.org/b"
+            targetNamespace="http://tests.python-zeep.org/b"
+            xmlns:c="http://tests.python-zeep.org/c"
+            elementFormDefault="qualified">
+
+            <xs:import
+                schemaLocation="http://tests.python-zeep.org/c.xsd"
+                namespace="http://tests.python-zeep.org/c"/>
+        </xs:schema>
+    """.strip())
+
+    schema_c = etree.fromstring("""
+        <?xml version="1.0"?>
+        <xs:schema
+            xmlns:xs="http://www.w3.org/2001/XMLSchema"
+            xmlns:tns="http://tests.python-zeep.org/c"
+            targetNamespace="http://tests.python-zeep.org/c"
+            elementFormDefault="qualified">
+
+            <xs:import
+                schemaLocation="http://tests.python-zeep.org/a.xsd"
+                namespace="http://tests.python-zeep.org/a"/>
+        </xs:schema>
+    """.strip())
+
+    transport = DummyTransport()
+    transport.bind('http://tests.python-zeep.org/a.xsd', schema_a)
+    transport.bind('http://tests.python-zeep.org/b.xsd', schema_b)
+    transport.bind('http://tests.python-zeep.org/c.xsd', schema_c)
+    xsd.Schema(schema_a, transport=transport, location='http://tests.python-zeep.org/a.xsd')
+
+
+def test_get_type_through_import():
+    schema_a = etree.fromstring("""
+        <?xml version="1.0"?>
+        <xs:schema
+            xmlns:xs="http://www.w3.org/2001/XMLSchema"
+            xmlns:tns="http://tests.python-zeep.org/a"
+            targetNamespace="http://tests.python-zeep.org/a"
+            xmlns:b="http://tests.python-zeep.org/b"
+            elementFormDefault="qualified">
+
+            <xs:import
+                schemaLocation="http://tests.python-zeep.org/b.xsd"
+                namespace="http://tests.python-zeep.org/b"/>
+            <xs:element name="foo" type="b:bar"/>
+
+        </xs:schema>
+    """.strip())
+
+    schema_b = etree.fromstring("""
+        <?xml version="1.0"?>
+        <xs:schema
+            xmlns:xs="http://www.w3.org/2001/XMLSchema"
+            xmlns:tns="http://tests.python-zeep.org/b"
+            targetNamespace="http://tests.python-zeep.org/b"
+            xmlns:c="http://tests.python-zeep.org/c"
+            elementFormDefault="qualified">
+
+            <xs:complexType name="bar"/>
+
+        </xs:schema>
+    """.strip())
+
+    transport = DummyTransport()
+    transport.bind('http://tests.python-zeep.org/a.xsd', schema_a)
+    transport.bind('http://tests.python-zeep.org/b.xsd', schema_b)
+    xsd.Schema(schema_a, transport=transport)
+
+
+def test_duplicate_target_namespace():
+    schema_a = etree.fromstring("""
+        <?xml version="1.0"?>
+        <xs:schema
+            xmlns:xs="http://www.w3.org/2001/XMLSchema"
+            xmlns:tns="http://tests.python-zeep.org/a"
+            targetNamespace="http://tests.python-zeep.org/a"
+            elementFormDefault="qualified">
+
+            <xs:import
+                schemaLocation="http://tests.python-zeep.org/b.xsd"
+                namespace="http://tests.python-zeep.org/duplicate"/>
+            <xs:import
+                schemaLocation="http://tests.python-zeep.org/c.xsd"
+                namespace="http://tests.python-zeep.org/duplicate"/>
+        </xs:schema>
+    """.strip())
+
+    schema_b = etree.fromstring("""
+        <?xml version="1.0"?>
+        <xsd:schema
+            xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+            targetNamespace="http://tests.python-zeep.org/duplicate"
+            elementFormDefault="qualified">
+        </xsd:schema>
+    """.strip())
+
+    schema_c = etree.fromstring("""
+        <?xml version="1.0"?>
+        <xsd:schema
+            xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+            targetNamespace="http://tests.python-zeep.org/duplicate"
+            elementFormDefault="qualified">
+        </xsd:schema>
+    """.strip())
+
+    transport = DummyTransport()
+    transport.bind('http://tests.python-zeep.org/a.xsd', schema_a)
+    transport.bind('http://tests.python-zeep.org/b.xsd', schema_b)
+    transport.bind('http://tests.python-zeep.org/c.xsd', schema_c)
+    with pytest.warns(ZeepWarning):
+        xsd.Schema(schema_a, transport=transport)
+
+
+def test_multiple_no_namespace():
+    node_a = etree.fromstring("""
+        <?xml version="1.0"?>
+        <xsd:schema
+            xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+            xmlns:tns="http://tests.python-zeep.org/a"
+            targetNamespace="http://tests.python-zeep.org/a"
+            elementFormDefault="qualified">
+
+          <xsd:import schemaLocation="http://tests.python-zeep.org/b.xsd"/>
+          <xsd:import schemaLocation="http://tests.python-zeep.org/c.xsd"/>
+        </xsd:schema>
+    """.strip())
+
+    node_b = etree.fromstring("""
+        <?xml version="1.0"?>
+        <xsd:schema
+            xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+            elementFormDefault="qualified">
+        </xsd:schema>
+    """.strip())
+
+    transport = DummyTransport()
+    transport.bind('http://tests.python-zeep.org/b.xsd', node_b)
+    transport.bind('http://tests.python-zeep.org/c.xsd', node_b)
+    with pytest.warns(ZeepWarning):
+        xsd.Schema(node_a, transport=transport)
+
+
+def test_multiple_only_target_ns():
+    node_a = etree.fromstring("""
+        <?xml version="1.0"?>
+        <xsd:schema
+            xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+            xmlns:tns="http://tests.python-zeep.org/a"
+            targetNamespace="http://tests.python-zeep.org/a"
+            elementFormDefault="qualified">
+
+          <xsd:import schemaLocation="http://tests.python-zeep.org/b.xsd"/>
+          <xsd:import schemaLocation="http://tests.python-zeep.org/c.xsd"/>
+        </xsd:schema>
+    """.strip())
+
+    node_b = etree.fromstring("""
+        <?xml version="1.0"?>
+        <xsd:schema
+            xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+            elementFormDefault="qualified"
+            targetNamespace="http://tests.python-zeep.org/duplicate-ns">
+        </xsd:schema>
+    """.strip())
+
+    transport = DummyTransport()
+    transport.bind('http://tests.python-zeep.org/b.xsd', node_b)
+    transport.bind('http://tests.python-zeep.org/c.xsd', node_b)
+    with pytest.warns(ZeepWarning):
+        xsd.Schema(node_a, transport=transport)
+
+
+def test_schema_error_handling():
+    node_a = etree.fromstring("""
+        <?xml version="1.0"?>
+        <xs:schema
+            xmlns:xs="http://www.w3.org/2001/XMLSchema"
+            xmlns:tns="http://tests.python-zeep.org/a"
+            targetNamespace="http://tests.python-zeep.org/a"
+            xmlns:b="http://tests.python-zeep.org/b"
+            elementFormDefault="qualified">
+
+        </xs:schema>
+    """.strip())
+    transport = DummyTransport()
+    schema = xsd.Schema(node_a, transport=transport)
+
+    with pytest.raises(ValueError):
+        schema.get_element('nonexisting:something')
+    with pytest.raises(ValueError):
+        schema.get_type('nonexisting:something')
+    with pytest.raises(exceptions.NamespaceError):
+        schema.get_element('{nonexisting}something')
+    with pytest.raises(exceptions.NamespaceError):
+        schema.get_type('{nonexisting}something')
+    with pytest.raises(exceptions.LookupError):
+        schema.get_element('ns0:something')
+    with pytest.raises(exceptions.LookupError):
+        schema.get_type('ns0:something')
+
+
+def test_schema_import_xmlsoap():
+    node_a = etree.fromstring("""
+        <?xml version="1.0"?>
+        <xsd:schema
+            xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+            xmlns:tns="http://tests.python-zeep.org/a"
+            targetNamespace="http://tests.python-zeep.org/a"
+            xmlns:b="http://tests.python-zeep.org/b"
+            elementFormDefault="qualified">
+          <xsd:import namespace="http://schemas.xmlsoap.org/soap/encoding/"/>
+        </xsd:schema>
+    """.strip())
+    transport = DummyTransport()
+    xsd.Schema(node_a, transport=transport)
+
+
+def test_schema_import_unresolved():
+    node_a = etree.fromstring("""
+        <?xml version="1.0"?>
+        <xsd:schema
+            xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+            xmlns:tns="http://tests.python-zeep.org/a"
+            targetNamespace="http://tests.python-zeep.org/a"
+            xmlns:b="http://tests.python-zeep.org/b"
+            elementFormDefault="qualified">
+          <xsd:import namespace="http://schemas.xmlsoap.org/soap/encoding/"/>
+        </xsd:schema>
+    """.strip())
+    transport = DummyTransport()
+    xsd.Schema(node_a, transport=transport)
+
+
+def test_no_target_namespace():
+    node_a = etree.fromstring("""
+        <?xml version="1.0"?>
+        <xsd:schema
+            xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+            xmlns:tns="http://tests.python-zeep.org/a"
+            targetNamespace="http://tests.python-zeep.org/a"
+            elementFormDefault="qualified">
+
+          <xsd:import schemaLocation="http://tests.python-zeep.org/b.xsd"/>
+
+          <xsd:element name="container">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element ref="bla"/>
+              </xsd:sequence>
+            </xsd:complexType>
+          </xsd:element>
+
+        </xsd:schema>
+    """.strip())
+
+    node_b = etree.fromstring("""
+        <?xml version="1.0"?>
+        <xsd:schema
+            xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+            elementFormDefault="qualified">
+            <xsd:element name="bla" type="xsd:string"/>
+        </xsd:schema>
+    """.strip())
+
+    transport = DummyTransport()
+    transport.bind('http://tests.python-zeep.org/b.xsd', node_b)
+    xsd.Schema(node_a, transport=transport)
+
+
+def test_include_recursion():
+    node_a = etree.fromstring("""
+        <?xml version="1.0"?>
+        <xs:schema
+            xmlns:xs="http://www.w3.org/2001/XMLSchema"
+            xmlns:tns="http://tests.python-zeep.org/a"
+            targetNamespace="http://tests.python-zeep.org/a"
+            xmlns:b="http://tests.python-zeep.org/b"
+            elementFormDefault="qualified">
+
+            <xs:import
+                schemaLocation="http://tests.python-zeep.org/b.xsd"
+                namespace="http://tests.python-zeep.org/b"/>
+
+        </xs:schema>
+    """.strip())
+
+    node_b = etree.fromstring("""
+        <?xml version="1.0"?>
+        <xs:schema
+            xmlns:xs="http://www.w3.org/2001/XMLSchema"
+            xmlns:tns="http://tests.python-zeep.org/b"
+            targetNamespace="http://tests.python-zeep.org/b"
+            elementFormDefault="qualified">
+
+            <xs:include schemaLocation="http://tests.python-zeep.org/c.xsd"/>
+            <xs:element name="bar" type="xs:string"/>
+        </xs:schema>
+    """.strip())
+
+    node_c = etree.fromstring("""
+        <?xml version="1.0"?>
+        <xs:schema
+            xmlns:xs="http://www.w3.org/2001/XMLSchema"
+            xmlns:tns="http://tests.python-zeep.org/b"
+            targetNamespace="http://tests.python-zeep.org/b"
+            elementFormDefault="qualified">
+
+            <xs:include schemaLocation="http://tests.python-zeep.org/b.xsd"/>
+
+            <xs:element name="foo" type="xs:string"/>
+        </xs:schema>
+    """.strip())
+
+    transport = DummyTransport()
+    transport.bind('http://tests.python-zeep.org/b.xsd', node_b)
+    transport.bind('http://tests.python-zeep.org/c.xsd', node_c)
+
+    schema = xsd.Schema(node_a, transport=transport)
+    schema.get_element('{http://tests.python-zeep.org/b}foo')
+    schema.get_element('{http://tests.python-zeep.org/b}bar')
+
+
+def test_merge():
+    node_a = etree.fromstring("""
+        <?xml version="1.0"?>
+        <xs:schema
+            xmlns:xs="http://www.w3.org/2001/XMLSchema"
+            xmlns:tns="http://tests.python-zeep.org/a"
+            targetNamespace="http://tests.python-zeep.org/a"
+            xmlns:b="http://tests.python-zeep.org/b"
+            elementFormDefault="qualified">
+          <xs:element name="foo" type="xs:string"/>
+        </xs:schema>
+    """.strip())
+
+    node_b = etree.fromstring("""
+        <?xml version="1.0"?>
+        <xs:schema
+            xmlns:xs="http://www.w3.org/2001/XMLSchema"
+            xmlns:tns="http://tests.python-zeep.org/b"
+            targetNamespace="http://tests.python-zeep.org/b"
+            elementFormDefault="qualified">
+          <xs:element name="foo" type="xs:int"/>
+        </xs:schema>
+    """.strip())
+
+    schema_a = xsd.Schema(node_a)
+    schema_b = xsd.Schema(node_b)
+    schema_a.merge(schema_b)
+
+    schema_a.get_element('{http://tests.python-zeep.org/a}foo')
+    schema_a.get_element('{http://tests.python-zeep.org/b}foo')
diff --git a/tests/test_xsd_signatures.py b/tests/test_xsd_signatures.py
new file mode 100644
index 0000000..0040d6a
--- /dev/null
+++ b/tests/test_xsd_signatures.py
@@ -0,0 +1,187 @@
+from lxml import etree
+
+from zeep import xsd
+
+
+def test_signature_complex_type_choice():
+    custom_type = xsd.Element(
+        etree.QName('http://tests.python-zeep.org/', 'authentication'),
+        xsd.ComplexType(
+            xsd.Choice([
+                xsd.Element(
+                    etree.QName('http://tests.python-zeep.org/', 'item_1'),
+                    xsd.String()),
+                xsd.Element(
+                    etree.QName('http://tests.python-zeep.org/', 'item_2'),
+                    xsd.String()),
+            ])
+        ))
+    assert custom_type.signature() == '({item_1: xsd:string} | {item_2: xsd:string})'
+
+
+def test_signature_complex_type_choice_sequence():
+    custom_type = xsd.Element(
+        etree.QName('http://tests.python-zeep.org/', 'authentication'),
+        xsd.ComplexType(
+            xsd.Choice([
+                xsd.Element(
+                    etree.QName('http://tests.python-zeep.org/', 'item_1'),
+                    xsd.String()),
+                xsd.Sequence([
+                    xsd.Element(
+                        etree.QName('http://tests.python-zeep.org/', 'item_2_1'),
+                        xsd.String()),
+                    xsd.Element(
+                        etree.QName('http://tests.python-zeep.org/', 'item_2_2'),
+                        xsd.String()),
+                ])
+            ])
+        ))
+    assert custom_type.signature() == (
+        '({item_1: xsd:string} | {item_2_1: xsd:string, item_2_2: xsd:string})')
+
+
+def test_signature_nested_sequences():
+    custom_type = xsd.Element(
+        etree.QName('http://tests.python-zeep.org/', 'authentication'),
+        xsd.ComplexType(
+            xsd.Sequence([
+                xsd.Element(
+                    etree.QName('http://tests.python-zeep.org/', 'item_1'),
+                    xsd.String()),
+                xsd.Element(
+                    etree.QName('http://tests.python-zeep.org/', 'item_2'),
+                    xsd.String()),
+                xsd.Sequence([
+                    xsd.Element(
+                        etree.QName('http://tests.python-zeep.org/', 'item_3'),
+                        xsd.String()),
+                    xsd.Element(
+                        etree.QName('http://tests.python-zeep.org/', 'item_4'),
+                        xsd.String()),
+                ]),
+                xsd.Choice([
+                    xsd.Element(
+                        etree.QName('http://tests.python-zeep.org/', 'item_5'),
+                        xsd.String()),
+                    xsd.Element(
+                        etree.QName('http://tests.python-zeep.org/', 'item_6'),
+                        xsd.String()),
+                    xsd.Sequence([
+                        xsd.Element(
+                            etree.QName('http://tests.python-zeep.org/', 'item_5'),
+                            xsd.String()),
+                        xsd.Element(
+                            etree.QName('http://tests.python-zeep.org/', 'item_6'),
+                            xsd.String()),
+                    ])
+                ])
+            ])
+        ))
+
+    assert custom_type.signature() == (
+        'item_1: xsd:string, item_2: xsd:string, item_3: xsd:string, item_4: xsd:string, ({item_5: xsd:string} | {item_6: xsd:string} | {item_5: xsd:string, item_6: xsd:string})'  # noqa
+    )
+
+
+def test_signature_nested_sequences_multiple():
+    custom_type = xsd.Element(
+        etree.QName('http://tests.python-zeep.org/', 'authentication'),
+        xsd.ComplexType(
+            xsd.Sequence([
+                xsd.Element(
+                    etree.QName('http://tests.python-zeep.org/', 'item_1'),
+                    xsd.String()),
+                xsd.Element(
+                    etree.QName('http://tests.python-zeep.org/', 'item_2'),
+                    xsd.String()),
+                xsd.Sequence([
+                    xsd.Element(
+                        etree.QName('http://tests.python-zeep.org/', 'item_3'),
+                        xsd.String()),
+                    xsd.Element(
+                        etree.QName('http://tests.python-zeep.org/', 'item_4'),
+                        xsd.String()),
+                ]),
+                xsd.Choice([
+                    xsd.Element(
+                        etree.QName('http://tests.python-zeep.org/', 'item_5'),
+                        xsd.String()),
+                    xsd.Element(
+                        etree.QName('http://tests.python-zeep.org/', 'item_6'),
+                        xsd.String()),
+                    xsd.Sequence([
+                        xsd.Element(
+                            etree.QName('http://tests.python-zeep.org/', 'item_5'),
+                            xsd.String()),
+                        xsd.Element(
+                            etree.QName('http://tests.python-zeep.org/', 'item_6'),
+                            xsd.String()),
+                    ])
+                ], min_occurs=2, max_occurs=3)
+            ])
+        ))
+
+    assert custom_type.signature() == (
+        'item_1: xsd:string, item_2: xsd:string, item_3: xsd:string, item_4: xsd:string, _value_1: ({item_5: xsd:string} | {item_6: xsd:string} | {item_5: xsd:string, item_6: xsd:string})[]'  # noqa
+    )
+
+
+def test_signature_complex_type_any():
+    custom_type = xsd.Element(
+        etree.QName('http://tests.python-zeep.org/', 'authentication'),
+        xsd.ComplexType(
+            xsd.Choice([
+                xsd.Element(
+                    etree.QName('http://tests.python-zeep.org/', 'item_1'),
+                    xsd.String()),
+                xsd.Any()
+            ])
+        ))
+    assert custom_type.signature() == '({item_1: xsd:string} | {_value_1: ANY})'
+    custom_type(item_1='foo')
+
+
+def test_signature_complex_type_sequence_with_any():
+    custom_type = xsd.Element(
+        etree.QName('http://tests.python-zeep.org/', 'authentication'),
+        xsd.ComplexType(
+            xsd.Choice([
+                xsd.Element(
+                    etree.QName('http://tests.python-zeep.org/', 'item_1'),
+                    xsd.String()),
+                xsd.Element(
+                    etree.QName('http://tests.python-zeep.org/', 'item_2'),
+                    xsd.ComplexType(
+                        xsd.Sequence([
+                            xsd.Any()
+                        ])
+                    )
+                )
+            ])
+        ))
+    assert custom_type.signature() == (
+        '({item_1: xsd:string} | {item_2: {_value_1: ANY}})')
+
+
+def test_signature_complex_type_sequence_with_anys():
+    custom_type = xsd.Element(
+        etree.QName('http://tests.python-zeep.org/', 'authentication'),
+        xsd.ComplexType(
+            xsd.Choice([
+                xsd.Element(
+                    etree.QName('http://tests.python-zeep.org/', 'item_1'),
+                    xsd.String()),
+                xsd.Element(
+                    etree.QName('http://tests.python-zeep.org/', 'item_2'),
+                    xsd.ComplexType(
+                        xsd.Sequence([
+                            xsd.Any(),
+                            xsd.Any(),
+                        ])
+                    )
+                )
+            ])
+        ))
+    assert custom_type.signature() == (
+        '({item_1: xsd:string} | {item_2: {_value_1: ANY, _value_2: ANY}})')
diff --git a/tests/test_xsd_simple_types.py b/tests/test_xsd_simple_types.py
new file mode 100644
index 0000000..fe22a4c
--- /dev/null
+++ b/tests/test_xsd_simple_types.py
@@ -0,0 +1,193 @@
+from lxml import etree
+
+from tests.utils import assert_nodes_equal, load_xml
+from zeep import xsd
+
+
+def test_simple_type():
+    schema = xsd.Schema(load_xml("""
+        <?xml version="1.0"?>
+        <schema xmlns="http://www.w3.org/2001/XMLSchema"
+                xmlns:tns="http://tests.python-zeep.org/"
+                targetNamespace="http://tests.python-zeep.org/"
+                elementFormDefault="qualified">
+          <element name="item">
+            <complexType>
+              <sequence>
+                <element name="something" type="long"/>
+              </sequence>
+            </complexType>
+          </element>
+        </schema>
+    """))
+
+    item_cls = schema.get_element('{http://tests.python-zeep.org/}item')
+    item = item_cls(something=12345678901234567890)
+
+    node = etree.Element('document')
+    item_cls.render(node, item)
+    expected = """
+        <document>
+          <ns0:item xmlns:ns0="http://tests.python-zeep.org/">
+            <ns0:something>12345678901234567890</ns0:something>
+          </ns0:item>
+        </document>
+    """
+    assert_nodes_equal(expected, node)
+    item = item_cls.parse(node.getchildren()[0], schema)
+    assert item.something == 12345678901234567890
+
+
+def test_simple_type_optional():
+    schema = xsd.Schema(load_xml("""
+        <?xml version="1.0"?>
+        <schema xmlns="http://www.w3.org/2001/XMLSchema"
+                xmlns:tns="http://tests.python-zeep.org/"
+                targetNamespace="http://tests.python-zeep.org/"
+                elementFormDefault="qualified">
+          <element name="item">
+            <complexType>
+              <sequence>
+                <element name="something" type="long" minOccurs="0"/>
+              </sequence>
+            </complexType>
+          </element>
+        </schema>
+    """))
+
+    item_cls = schema.get_element('{http://tests.python-zeep.org/}item')
+    item = item_cls()
+    assert item.something is None
+
+    node = etree.Element('document')
+    item_cls.render(node, item)
+    expected = """
+        <document>
+          <ns0:item xmlns:ns0="http://tests.python-zeep.org/"/>
+        </document>
+    """
+    assert_nodes_equal(expected, node)
+
+    item = item_cls.parse(node.getchildren()[0], schema)
+    assert item.something is None
+
+
+def test_restriction_global():
+    schema = xsd.Schema(load_xml("""
+        <?xml version="1.0"?>
+        <schema xmlns="http://www.w3.org/2001/XMLSchema"
+                xmlns:tns="http://tests.python-zeep.org/"
+                targetNamespace="http://tests.python-zeep.org/"
+                elementFormDefault="qualified">
+          <simpleType name="foo">
+            <restriction base="integer">
+              <minInclusive value="0"/>
+              <maxInclusive value="100"/>
+            </restriction>
+          </simpleType>
+        </schema>
+    """))
+
+    type_cls = schema.get_type('{http://tests.python-zeep.org/}foo')
+    assert type_cls.qname.text == '{http://tests.python-zeep.org/}foo'
+
+
+def test_restriction_anon():
+    schema = xsd.Schema(load_xml("""
+        <?xml version="1.0"?>
+        <schema xmlns="http://www.w3.org/2001/XMLSchema"
+                xmlns:tns="http://tests.python-zeep.org/"
+                targetNamespace="http://tests.python-zeep.org/"
+                elementFormDefault="qualified">
+          <element name="something">
+            <simpleType>
+              <restriction base="integer">
+                <minInclusive value="0"/>
+                <maxInclusive value="100"/>
+              </restriction>
+            </simpleType>
+          </element>
+        </schema>
+    """))
+
+    element_cls = schema.get_element('{http://tests.python-zeep.org/}something')
+    assert element_cls.type.qname == etree.QName(
+        '{http://tests.python-zeep.org/}something')
+
+    obj = element_cls(75)
+
+    node = etree.Element('document')
+    element_cls.render(node, obj)
+    expected = """
+        <document>
+            <ns0:something xmlns:ns0="http://tests.python-zeep.org/">75</ns0:something>
+        </document>
+    """
+    assert_nodes_equal(expected, node)
+
+def test_simple_type_list():
+    schema = xsd.Schema(load_xml("""
+        <?xml version="1.0"?>
+        <schema xmlns="http://www.w3.org/2001/XMLSchema"
+                xmlns:tns="http://tests.python-zeep.org/"
+                targetNamespace="http://tests.python-zeep.org/"
+                elementFormDefault="qualified">
+
+          <simpleType name="values">
+            <list itemType="integer"/>
+          </simpleType>
+          <element name="something" type="tns:values"/>
+        </schema>
+    """))
+
+    element_cls = schema.get_element('{http://tests.python-zeep.org/}something')
+    obj = element_cls([1, 2, 3])
+    assert obj == [1, 2, 3]
+
+    node = etree.Element('document')
+    element_cls.render(node, obj)
+    expected = """
+        <document>
+            <ns0:something xmlns:ns0="http://tests.python-zeep.org/">1 2 3</ns0:something>
+        </document>
+    """
+    assert_nodes_equal(expected, node)
+
+
+def test_simple_type_list_custom_type():
+    schema = xsd.Schema(load_xml("""
+        <?xml version="1.0"?>
+        <schema xmlns="http://www.w3.org/2001/XMLSchema"
+                xmlns:tns="http://tests.python-zeep.org/"
+                targetNamespace="http://tests.python-zeep.org/"
+                elementFormDefault="qualified">
+
+          <simpleType name="CountryNameType">
+            <list>
+              <simpleType>
+                <restriction base="string">
+                  <enumeration value="None"/>
+                  <enumeration value="AlternateName"/>
+                  <enumeration value="City"/>
+                  <enumeration value="Code"/>
+                  <enumeration value="Country"/>
+                </restriction>
+              </simpleType>
+            </list>
+          </simpleType>
+          <element name="something" type="tns:CountryNameType"/>
+        </schema>
+    """))
+
+    element_cls = schema.get_element('{http://tests.python-zeep.org/}something')
+    obj = element_cls(['Code', 'City'])
+    assert obj == ['Code', 'City']
+
+    node = etree.Element('document')
+    element_cls.render(node, obj)
+    expected = """
+        <document>
+            <ns0:something xmlns:ns0="http://tests.python-zeep.org/">Code City</ns0:something>
+        </document>
+    """
+    assert_nodes_equal(expected, node)
diff --git a/tests/test_xsd_types.py b/tests/test_xsd_types.py
new file mode 100644
index 0000000..5e902fa
--- /dev/null
+++ b/tests/test_xsd_types.py
@@ -0,0 +1,80 @@
+import pytest
+import six
+from lxml import etree
+
+from zeep.xsd import types
+
+
+def test_base_type():
+    # Basically just for coverage... ;-)
+    base = types.Type()
+    with pytest.raises(NotImplementedError):
+        base.accept('x')
+
+    with pytest.raises(NotImplementedError):
+        base.parse_xmlelement(None)
+
+    with pytest.raises(NotImplementedError):
+        base.parsexml(None)
+
+    with pytest.raises(NotImplementedError):
+        base.render(None, None)
+
+    with pytest.raises(NotImplementedError):
+        base.resolve()
+
+    base.signature() == ''
+
+
+def test_simpletype_eq():
+    type_1 = types.SimpleType()
+    type_2 = types.SimpleType()
+
+    assert type_1 == type_2
+
+
+def test_simpletype_parse():
+    node = etree.Element('foobar')
+    item = types.SimpleType()
+
+    assert item.parse_xmlelement(node) is None
+
+
+def test_simpletype_xmlvalue():
+    item = types.SimpleType()
+
+    with pytest.raises(NotImplementedError):
+        item.xmlvalue(None)
+
+
+def test_simpletype_pythonvalue():
+    item = types.SimpleType()
+
+    with pytest.raises(NotImplementedError):
+        item.pythonvalue(None)
+
+
+def test_simpletype_call_wrong_arg_count():
+    item = types.SimpleType()
+
+    with pytest.raises(TypeError):
+        item('foo', 'bar')
+
+
+def test_simpletype_call_wrong_kwarg():
+    item = types.SimpleType()
+
+    with pytest.raises(TypeError):
+        item(uhhh='x')
+
+
+def test_simpletype_str():
+    item = types.SimpleType()
+    item.name = u'foobar'
+    assert six.text_type(item) == 'SimpleType(value)'
+
+
+def test_complextype_parse_xmlelement_no_childs():
+    xmlelement = etree.Element('foobar')
+    item = types.ComplexType()
+    assert item.parse_xmlelement(xmlelement, None) is None
diff --git a/tests/test_xsd_union.py b/tests/test_xsd_union.py
new file mode 100644
index 0000000..ad56f93
--- /dev/null
+++ b/tests/test_xsd_union.py
@@ -0,0 +1,91 @@
+import datetime
+import io
+
+from lxml import etree
+
+from tests.utils import DummyTransport, assert_nodes_equal, load_xml, render_node
+from zeep import xsd
+
+
+def test_union_same_types():
+    schema = xsd.Schema(load_xml("""
+        <?xml version="1.0"?>
+        <xsd:schema
+            xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+            xmlns:tns="http://tests.python-zeep.org/"
+            targetNamespace="http://tests.python-zeep.org/"
+            elementFormDefault="qualified">
+
+
+          <xsd:simpleType name="MMYY">
+            <xsd:restriction base="xsd:int"/>
+          </xsd:simpleType>
+
+          <xsd:simpleType name="MMYYYY">
+            <xsd:restriction base="xsd:int"/>
+          </xsd:simpleType>
+
+          <xsd:simpleType name="Date">
+            <xsd:union memberTypes="MMYY MMYYYY"/>
+          </xsd:simpleType>
+          <xsd:element name="item" type="tns:Date"/>
+        </xsd:schema>
+    """))
+
+    elm = schema.get_element('ns0:item')
+    node = render_node(elm, '102018')
+    expected = """
+        <document>
+          <ns0:item xmlns:ns0="http://tests.python-zeep.org/">102018</ns0:item>
+        </document>
+    """
+    assert_nodes_equal(expected, node)
+    value = elm.parse(node.getchildren()[0], schema)
+    assert value == 102018
+
+
+def test_union_mixed():
+    schema = xsd.Schema(load_xml("""
+        <?xml version="1.0"?>
+        <xsd:schema
+            xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+            xmlns:tns="http://tests.python-zeep.org/"
+            targetNamespace="http://tests.python-zeep.org/"
+            elementFormDefault="qualified">
+          <xsd:element name="item" type="tns:Date"/>
+          <xsd:simpleType name="Date">
+            <xsd:union memberTypes="xsd:date xsd:gYear xsd:gYearMonth MMYY MMYYYY"/>
+          </xsd:simpleType>
+          <xsd:simpleType name="MMYY">
+            <xsd:restriction base="xsd:string">
+              <xsd:pattern value="(0[123456789]|1[012]){1}\d{2}"/>
+            </xsd:restriction>
+          </xsd:simpleType>
+          <xsd:simpleType name="MMYYYY">
+            <xsd:restriction base="xsd:string">
+              <xsd:pattern value="(0[123456789]|1[012]){1}\d{4}"/>
+            </xsd:restriction>
+          </xsd:simpleType>
+        </xsd:schema>
+    """))
+
+    elm = schema.get_element('ns0:item')
+    node = render_node(elm, '102018')
+    expected = """
+        <document>
+          <ns0:item xmlns:ns0="http://tests.python-zeep.org/">102018</ns0:item>
+        </document>
+    """
+    assert_nodes_equal(expected, node)
+    value = elm.parse(node.getchildren()[0], schema)
+    assert value == '102018'
+
+    node = render_node(elm, '2018')
+    expected = """
+        <document>
+          <ns0:item xmlns:ns0="http://tests.python-zeep.org/">2018</ns0:item>
+        </document>
+    """
+    assert_nodes_equal(expected, node)
+    value = elm.parse(node.getchildren()[0], schema)
+    assert value == '2018'
diff --git a/tests/test_xsd_valueobjects.py b/tests/test_xsd_valueobjects.py
new file mode 100644
index 0000000..f9c6eaa
--- /dev/null
+++ b/tests/test_xsd_valueobjects.py
@@ -0,0 +1,403 @@
+import pytest
+import six
+
+from zeep import xsd
+from zeep.xsd import valueobjects
+
+
+def test_simple_args():
+    xsd_type = xsd.ComplexType(
+        xsd.Sequence([
+            xsd.Element('item_1', xsd.String()),
+            xsd.Element('item_2', xsd.String())
+        ]))
+    args = tuple(['value-1', 'value-2'])
+    kwargs = {}
+    result = valueobjects._process_signature(xsd_type, args, kwargs)
+    assert result == {
+        'item_1': 'value-1',
+        'item_2': 'value-2',
+    }
+
+
+def test_simple_args_attributes():
+    xsd_type = xsd.ComplexType(
+        xsd.Sequence([
+            xsd.Element('item_1', xsd.String()),
+            xsd.Element('item_2', xsd.String())
+        ]),
+        [
+            xsd.Attribute('attr_1', xsd.String())
+        ]
+    )
+    args = tuple(['value-1', 'value-2', 'bla'])
+    kwargs = {}
+    result = valueobjects._process_signature(xsd_type, args, kwargs)
+    assert result == {
+        'item_1': 'value-1',
+        'item_2': 'value-2',
+        'attr_1': 'bla',
+    }
+
+
+def test_simple_args_attributes_as_kwargs():
+    xsd_type = xsd.ComplexType(
+        xsd.Sequence([
+            xsd.Element('item_1', xsd.String()),
+            xsd.Element('item_2', xsd.String())
+        ]),
+        [
+            xsd.Attribute('attr_1', xsd.String())
+        ]
+    )
+    args = tuple(['value-1', 'value-2'])
+    kwargs = {'attr_1': 'bla'}
+    result = valueobjects._process_signature(xsd_type, args, kwargs)
+    assert result == {
+        'item_1': 'value-1',
+        'item_2': 'value-2',
+        'attr_1': 'bla',
+    }
+
+
+def test_simple_args_too_many():
+    xsd_type = xsd.ComplexType(
+        xsd.Sequence([
+            xsd.Element('item_1', xsd.String()),
+            xsd.Element('item_2', xsd.String())
+        ]))
+    args = tuple(['value-1', 'value-2', 'value-3'])
+    kwargs = {}
+
+    try:
+        valueobjects._process_signature(xsd_type, args, kwargs)
+    except TypeError as exc:
+        assert six.text_type(exc) == (
+            '__init__() takes at most 2 positional arguments (3 given)')
+    else:
+        assert False, "TypeError not raised"
+
+
+def test_simple_args_too_few():
+    xsd_type = xsd.ComplexType(
+        xsd.Sequence([
+            xsd.Element('item_1', xsd.String()),
+            xsd.Element('item_2', xsd.String())
+        ]))
+    args = tuple(['value-1'])
+    kwargs = {}
+    valueobjects._process_signature(xsd_type, args, kwargs)
+
+
+def test_simple_kwargs():
+    xsd_type = xsd.ComplexType(
+        xsd.Sequence([
+            xsd.Element('item_1', xsd.String()),
+            xsd.Element('item_2', xsd.String())
+        ]))
+    args = tuple([])
+    kwargs = {'item_1': 'value-1', 'item_2': 'value-2'}
+    result = valueobjects._process_signature(xsd_type, args, kwargs)
+    assert result == {
+        'item_1': 'value-1',
+        'item_2': 'value-2',
+    }
+
+
+def test_simple_mixed():
+    xsd_type = xsd.ComplexType(
+        xsd.Sequence([
+            xsd.Element('item_1', xsd.String()),
+            xsd.Element('item_2', xsd.String())
+        ]))
+    args = tuple(['value-1'])
+    kwargs = {'item_2': 'value-2'}
+    result = valueobjects._process_signature(xsd_type, args, kwargs)
+    assert result == {
+        'item_1': 'value-1',
+        'item_2': 'value-2',
+    }
+
+
+def test_choice():
+    xsd_type = xsd.ComplexType(
+        xsd.Sequence([
+            xsd.Choice([
+                xsd.Element('item_1', xsd.String()),
+                xsd.Element('item_2', xsd.String())
+            ])
+        ])
+    )
+    args = tuple([])
+    kwargs = {'item_2': 'value-2'}
+    result = valueobjects._process_signature(xsd_type, args, kwargs)
+    assert result == {'item_1': None, 'item_2': 'value-2'}
+
+
+def test_choice_max_occurs_simple_interface():
+    fields = xsd.ComplexType(
+        xsd.Sequence([
+            xsd.Choice([
+                xsd.Element('item_1', xsd.String()),
+                xsd.Element('item_2', xsd.String())
+            ], max_occurs=2)
+        ])
+    )
+    args = tuple([])
+    kwargs = {
+        '_value_1': [{'item_1': 'foo'}, {'item_2': 'bar'}]
+    }
+    result = valueobjects._process_signature(fields, args, kwargs)
+    assert result == {
+        '_value_1': [
+            {'item_1': 'foo'},
+            {'item_2': 'bar'},
+        ]
+    }
+
+
+def test_choice_max_occurs():
+    fields = xsd.ComplexType(
+        xsd.Sequence([
+            xsd.Choice([
+                xsd.Element('item_1', xsd.String()),
+                xsd.Element('item_2', xsd.String())
+            ], max_occurs=3)
+        ])
+    )
+    args = tuple([])
+    kwargs = {
+        '_value_1': [
+            {'item_1': 'foo'}, {'item_2': 'bar'}, {'item_1': 'bla'}
+        ]
+    }
+    result = valueobjects._process_signature(fields, args, kwargs)
+    assert result == {
+        '_value_1': [
+            {'item_1': 'foo'},
+            {'item_2': 'bar'},
+            {'item_1': 'bla'},
+        ]
+    }
+
+
+def test_choice_max_occurs_on_choice():
+    xsd_type = xsd.ComplexType(
+        xsd.Sequence([
+            xsd.Choice([
+                xsd.Element('item_1', xsd.String(), max_occurs=2),
+                xsd.Element('item_2', xsd.String())
+            ], max_occurs=2)
+        ])
+    )
+    args = tuple([])
+    kwargs = {
+        '_value_1': [
+            {'item_1': ['foo', 'bar']},
+            {'item_2': 'bla'},
+        ]
+    }
+    result = valueobjects._process_signature(xsd_type, args, kwargs)
+    assert result == {
+        '_value_1': [
+            {'item_1': ['foo', 'bar']},
+            {'item_2': 'bla'}
+        ]
+    }
+
+
+def test_choice_mixed():
+    xsd_type = xsd.ComplexType(
+        xsd.Sequence([
+            xsd.Choice([
+                xsd.Element('item_1', xsd.String()),
+                xsd.Element('item_2', xsd.String()),
+            ]),
+            xsd.Element('item_2', xsd.String())
+        ])
+    )
+    expected = '({item_1: xsd:string} | {item_2: xsd:string}), item_2__1: xsd:string'
+    assert xsd_type.signature() == expected
+
+    args = tuple([])
+    kwargs = {'item_1': 'value-1', 'item_2__1': 'value-2'}
+    result = valueobjects._process_signature(xsd_type, args, kwargs)
+    assert result == {
+        'item_1': 'value-1',
+        'item_2': None,
+        'item_2__1': 'value-2',
+    }
+
+
+def test_choice_sequences_simple():
+    xsd_type = xsd.ComplexType(
+        xsd.Sequence([
+            xsd.Choice([
+                xsd.Sequence([
+                    xsd.Element('item_1', xsd.String()),
+                    xsd.Element('item_2', xsd.String())
+                ]),
+                xsd.Sequence([
+                    xsd.Element('item_3', xsd.String()),
+                    xsd.Element('item_4', xsd.String())
+                ]),
+            ])
+        ])
+    )
+    args = tuple([])
+    kwargs = {'item_1': 'value-1', 'item_2': 'value-2'}
+    result = valueobjects._process_signature(xsd_type, args, kwargs)
+    assert result == {
+        'item_1': 'value-1',
+        'item_2': 'value-2',
+        'item_3': None,
+        'item_4': None,
+    }
+
+
+def test_choice_sequences_no_match():
+    xsd_type = xsd.ComplexType(
+        xsd.Sequence([
+            xsd.Choice([
+                xsd.Sequence([
+                    xsd.Element('item_1', xsd.String()),
+                    xsd.Element('item_2', xsd.String())
+                ]),
+                xsd.Sequence([
+                    xsd.Element('item_3', xsd.String()),
+                    xsd.Element('item_4', xsd.String())
+                ]),
+            ])
+        ])
+    )
+    args = tuple([])
+    with pytest.raises(TypeError):
+        kwargs = {'item_1': 'value-1', 'item_3': 'value-3'}
+        valueobjects._process_signature(xsd_type, args, kwargs)
+
+
+def test_choice_sequences_no_match_last():
+    xsd_type = xsd.ComplexType(
+        xsd.Sequence([
+            xsd.Choice([
+                xsd.Sequence([
+                    xsd.Element('item_1', xsd.String()),
+                    xsd.Element('item_2', xsd.String())
+                ]),
+                xsd.Sequence([
+                    xsd.Element('item_3', xsd.String()),
+                    xsd.Element('item_4', xsd.String())
+                ]),
+            ])
+        ])
+    )
+    args = tuple([])
+    with pytest.raises(TypeError):
+        kwargs = {'item_2': 'value-2', 'item_4': 'value-4'}
+        valueobjects._process_signature(xsd_type, args, kwargs)
+
+
+def test_choice_sequences_no_match_nested():
+    xsd_type = xsd.ComplexType(
+        xsd.Sequence([
+            xsd.Choice([
+                xsd.Sequence([
+                    xsd.Element('item_1', xsd.String()),
+                    xsd.Element('item_2', xsd.String())
+                ]),
+            ])
+        ])
+    )
+    args = tuple([])
+    kwargs = {'item_1': 'value-1'}
+    value = valueobjects._process_signature(xsd_type, args, kwargs)
+    assert value == {
+        'item_1': 'value-1',
+        'item_2': None,
+    }
+
+
+def test_choice_sequences_optional_elms():
+    xsd_type = xsd.ComplexType(
+        xsd.Sequence([
+            xsd.Choice([
+                xsd.Sequence([
+                    xsd.Element('item_1', xsd.String()),
+                    xsd.Element('item_2', xsd.String(), min_occurs=0)
+                ]),
+                xsd.Sequence([
+                    xsd.Element('item_1', xsd.String()),
+                    xsd.Element('item_2', xsd.String()),
+                    xsd.Element('item_3', xsd.String())
+                ]),
+            ])
+        ])
+    )
+    args = tuple([])
+    kwargs = {'item_1': 'value-1'}
+    result = valueobjects._process_signature(xsd_type, args, kwargs)
+    assert result == {
+        'item_1': 'value-1',
+        'item_2': None,
+        'item_3': None,
+    }
+
+
+def test_choice_sequences_max_occur():
+    xsd_type = xsd.ComplexType(
+        xsd.Sequence([
+            xsd.Choice([
+                xsd.Sequence([
+                    xsd.Element('item_1', xsd.String()),
+                    xsd.Element('item_2', xsd.String())
+                ]),
+                xsd.Sequence([
+                    xsd.Element('item_2', xsd.String()),
+                    xsd.Element('item_3', xsd.String()),
+                ]),
+            ], max_occurs=2)
+        ]),
+    )
+    args = tuple([])
+    kwargs = {
+        '_value_1': [
+            {'item_1': 'value-1', 'item_2': 'value-2'},
+            {'item_2': 'value-2', 'item_3': 'value-3'},
+        ]
+    }
+
+    result = valueobjects._process_signature(xsd_type, args, kwargs)
+    assert result == {
+        '_value_1': [
+            {'item_1': 'value-1', 'item_2': 'value-2'},
+            {'item_2': 'value-2', 'item_3': 'value-3'},
+        ]
+    }
+
+
+def test_choice_sequences_init_dict():
+    xsd_type = xsd.ComplexType(
+        xsd.Sequence([
+            xsd.Choice([
+                xsd.Sequence([
+                    xsd.Element('item_1', xsd.String()),
+                    xsd.Element('item_2', xsd.String())
+                ]),
+                xsd.Sequence([
+                    xsd.Element('item_2', xsd.String()),
+                    xsd.Element('item_3', xsd.String()),
+                ]),
+            ], max_occurs=2)
+        ]),
+    )
+    args = tuple([])
+    kwargs = {
+        '_value_1': {'item_1': 'value-1', 'item_2': 'value-2'},
+    }
+
+    result = valueobjects._process_signature(xsd_type, args, kwargs)
+    assert result == {
+        '_value_1': [
+            {'item_1': 'value-1', 'item_2': 'value-2'}
+        ]
+    }
diff --git a/tests/wsdl_files/soap-enc.xsd b/tests/wsdl_files/soap-enc.xsd
new file mode 100644
index 0000000..de4a44a
--- /dev/null
+++ b/tests/wsdl_files/soap-enc.xsd
@@ -0,0 +1,535 @@
+<?xml version='1.0' encoding='UTF-8' ?>
+
+<!-- Schema for the SOAP/1.1 encoding
+
+Portions © 2001 DevelopMentor. 
+© 2001 W3C (Massachusetts Institute of Technology, Institut National de Recherche en Informatique et en Automatique, Keio University). All Rights Reserved.  
+ 
+This document is governed by the W3C Software License [1] as described in the FAQ [2].
+[1] http://www.w3.org/Consortium/Legal/copyright-software-19980720
+[2] http://www.w3.org/Consortium/Legal/IPR-FAQ-20000620.html#DTD 
+By obtaining, using and/or copying this work, you (the licensee) agree that you have read, understood, and will comply with the following terms and conditions:
+
+Permission to use, copy, modify, and distribute this software and its documentation, with or without modification,  for any purpose and without fee or royalty is hereby granted, provided that you include the following on ALL copies of the software and documentation or portions thereof, including modifications, that you make:
+
+1.  The full text of this NOTICE in a location viewable to users of the redistributed or derivative work. 
+
+2.  Any pre-existing intellectual property disclaimers, notices, or terms and conditions. If none exist, a short notice of the following form (hypertext is preferred, text is permitted) should be used within the body of any redistributed or derivative code: "Copyright © 2001 World Wide Web Consortium, (Massachusetts Institute of Technology, Institut National de Recherche en Informatique et en Automatique, Keio University). All Rights Reserved. http://www.w3.org/Consortium/Legal/" 
+
+3.  Notice of any changes or modifications to the W3C files, including the date changes were made. (We recommend you provide URIs to the location from which the code is derived.)   
+
+Original W3C files; http://www.w3.org/2001/06/soap-encoding
+Changes made: 
+     - reverted namespace to http://schemas.xmlsoap.org/soap/encoding/
+     - reverted root to only allow 0 and 1 as lexical values
+	 - removed default value from root attribute declaration
+
+THIS SOFTWARE AND DOCUMENTATION IS PROVIDED "AS IS," AND COPYRIGHT HOLDERS MAKE NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO, WARRANTIES OF MERCHANTABILITY OR FITNESS FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF THE SOFTWARE OR DOCUMENTATION WILL NOT INFRINGE ANY THIRD PARTY PATENTS, COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS.
+
+COPYRIGHT HOLDERS WILL NOT BE LIABLE FOR ANY DIRECT, INDIRECT, SPECIAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF ANY USE OF THE SOFTWARE OR DOCUMENTATION.
+
+The name and trademarks of copyright holders may NOT be used in advertising or publicity pertaining to the software without specific, written prior permission. Title to copyright in this software and any associated documentation will at all times remain with copyright holders.
+
+-->
+<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
+           xmlns:tns="http://schemas.xmlsoap.org/soap/encoding/"
+           targetNamespace="http://schemas.xmlsoap.org/soap/encoding/" >
+        
+ <xs:attribute name="root" >
+   <xs:annotation>
+     <xs:documentation>
+	   'root' can be used to distinguish serialization roots from other
+       elements that are present in a serialization but are not roots of
+       a serialized value graph 
+	 </xs:documentation>
+   </xs:annotation>
+   <xs:simpleType>
+     <xs:restriction base='xs:boolean'>
+	   <xs:pattern value='0|1' />
+	 </xs:restriction>
+   </xs:simpleType>
+ </xs:attribute>
+
+  <xs:attributeGroup name="commonAttributes" >
+    <xs:annotation>
+	  <xs:documentation>
+	    Attributes common to all elements that function as accessors or 
+        represent independent (multi-ref) values.  The href attribute is
+        intended to be used in a manner like CONREF.  That is, the element
+        content should be empty iff the href attribute appears
+	  </xs:documentation>
+	</xs:annotation>
+    <xs:attribute name="id" type="xs:ID" />
+    <xs:attribute name="href" type="xs:anyURI" />
+    <xs:anyAttribute namespace="##other" processContents="lax" />
+  </xs:attributeGroup>
+
+  <!-- Global Attributes.  The following attributes are intended to be usable via qualified attribute names on any complex type referencing them. -->
+       
+  <!-- Array attributes. Needed to give the type and dimensions of an array's contents, and the offset for partially-transmitted arrays. -->
+   
+  <xs:simpleType name="arrayCoordinate" >
+    <xs:restriction base="xs:string" />
+  </xs:simpleType>
+          
+  <xs:attribute name="arrayType" type="xs:string" />
+  <xs:attribute name="offset" type="tns:arrayCoordinate" />
+  
+  <xs:attributeGroup name="arrayAttributes" >
+    <xs:attribute ref="tns:arrayType" />
+    <xs:attribute ref="tns:offset" />
+  </xs:attributeGroup>    
+  
+  <xs:attribute name="position" type="tns:arrayCoordinate" /> 
+  
+  <xs:attributeGroup name="arrayMemberAttributes" >
+    <xs:attribute ref="tns:position" />
+  </xs:attributeGroup>    
+
+  <xs:group name="Array" >
+    <xs:sequence>
+      <xs:any namespace="##any" minOccurs="0" maxOccurs="unbounded" processContents="lax" />
+	</xs:sequence>
+  </xs:group>
+
+  <xs:element name="Array" type="tns:Array" />
+  <xs:complexType name="Array" >
+    <xs:annotation>
+	  <xs:documentation>
+	   'Array' is a complex type for accessors identified by position 
+	  </xs:documentation>
+	</xs:annotation>
+    <xs:group ref="tns:Array" minOccurs="0" />
+    <xs:attributeGroup ref="tns:arrayAttributes" />
+    <xs:attributeGroup ref="tns:commonAttributes" />
+  </xs:complexType> 
+
+  <!-- 'Struct' is a complex type for accessors identified by name. 
+       Constraint: No element may be have the same name as any other,
+       nor may any element have a maxOccurs > 1. -->
+   
+  <xs:element name="Struct" type="tns:Struct" />
+
+  <xs:group name="Struct" >
+    <xs:sequence>
+      <xs:any namespace="##any" minOccurs="0" maxOccurs="unbounded" processContents="lax" />
+	</xs:sequence>
+  </xs:group>
+
+  <xs:complexType name="Struct" >
+    <xs:group ref="tns:Struct" minOccurs="0" />
+    <xs:attributeGroup ref="tns:commonAttributes"/>
+  </xs:complexType> 
+
+  <!-- 'Base64' can be used to serialize binary data using base64 encoding
+       as defined in RFC2045 but without the MIME line length limitation. -->
+
+  <xs:simpleType name="base64" >
+    <xs:restriction base="xs:base64Binary" />
+  </xs:simpleType>
+
+ <!-- Element declarations corresponding to each of the simple types in the 
+      XML Schemas Specification. -->
+
+  <xs:element name="duration" type="tns:duration" />
+  <xs:complexType name="duration" >
+    <xs:simpleContent>
+      <xs:extension base="xs:duration" >
+        <xs:attributeGroup ref="tns:commonAttributes" />
+      </xs:extension>
+    </xs:simpleContent>
+  </xs:complexType>
+
+  <xs:element name="dateTime" type="tns:dateTime" />
+  <xs:complexType name="dateTime" >
+    <xs:simpleContent>
+      <xs:extension base="xs:dateTime" >
+        <xs:attributeGroup ref="tns:commonAttributes" />
+      </xs:extension>
+    </xs:simpleContent>
+  </xs:complexType>
+
+
+
+  <xs:element name="NOTATION" type="tns:NOTATION" />
+  <xs:complexType name="NOTATION" >
+    <xs:simpleContent>
+      <xs:extension base="xs:QName" >
+        <xs:attributeGroup ref="tns:commonAttributes" />
+      </xs:extension>
+    </xs:simpleContent>
+  </xs:complexType>
+  
+
+  <xs:element name="time" type="tns:time" />
+  <xs:complexType name="time" >
+    <xs:simpleContent>
+      <xs:extension base="xs:time" >
+        <xs:attributeGroup ref="tns:commonAttributes" />
+      </xs:extension>
+    </xs:simpleContent>
+  </xs:complexType>
+
+  <xs:element name="date" type="tns:date" />
+  <xs:complexType name="date" >
+    <xs:simpleContent>
+      <xs:extension base="xs:date" >
+        <xs:attributeGroup ref="tns:commonAttributes" />
+      </xs:extension>
+    </xs:simpleContent>
+  </xs:complexType>
+
+  <xs:element name="gYearMonth" type="tns:gYearMonth" />
+  <xs:complexType name="gYearMonth" >
+    <xs:simpleContent>
+      <xs:extension base="xs:gYearMonth" >
+        <xs:attributeGroup ref="tns:commonAttributes" />
+      </xs:extension>
+    </xs:simpleContent>
+  </xs:complexType>
+
+  <xs:element name="gYear" type="tns:gYear" />
+  <xs:complexType name="gYear" >
+    <xs:simpleContent>
+      <xs:extension base="xs:gYear" >
+        <xs:attributeGroup ref="tns:commonAttributes" />
+      </xs:extension>
+    </xs:simpleContent>
+  </xs:complexType>
+
+  <xs:element name="gMonthDay" type="tns:gMonthDay" />
+  <xs:complexType name="gMonthDay" >
+    <xs:simpleContent>
+      <xs:extension base="xs:gMonthDay" >
+        <xs:attributeGroup ref="tns:commonAttributes" />
+      </xs:extension>
+    </xs:simpleContent>
+  </xs:complexType>
+
+  <xs:element name="gDay" type="tns:gDay" />
+  <xs:complexType name="gDay" >
+    <xs:simpleContent>
+      <xs:extension base="xs:gDay" >
+        <xs:attributeGroup ref="tns:commonAttributes" />
+      </xs:extension>
+    </xs:simpleContent>
+  </xs:complexType>
+
+  <xs:element name="gMonth" type="tns:gMonth" />
+  <xs:complexType name="gMonth" >
+    <xs:simpleContent>
+      <xs:extension base="xs:gMonth" >
+        <xs:attributeGroup ref="tns:commonAttributes" />
+      </xs:extension>
+    </xs:simpleContent>
+  </xs:complexType>
+  
+  <xs:element name="boolean" type="tns:boolean" />
+  <xs:complexType name="boolean" >
+    <xs:simpleContent>
+      <xs:extension base="xs:boolean" >
+        <xs:attributeGroup ref="tns:commonAttributes" />
+      </xs:extension>
+    </xs:simpleContent>
+  </xs:complexType>
+
+  <xs:element name="base64Binary" type="tns:base64Binary" />
+  <xs:complexType name="base64Binary" >
+    <xs:simpleContent>
+      <xs:extension base="xs:base64Binary" >
+        <xs:attributeGroup ref="tns:commonAttributes" />
+      </xs:extension>
+    </xs:simpleContent>
+  </xs:complexType>
+
+  <xs:element name="hexBinary" type="tns:hexBinary" />
+  <xs:complexType name="hexBinary" >
+    <xs:simpleContent>
+     <xs:extension base="xs:hexBinary" >
+       <xs:attributeGroup ref="tns:commonAttributes" />
+     </xs:extension>
+    </xs:simpleContent>
+  </xs:complexType>
+
+  <xs:element name="float" type="tns:float" />
+  <xs:complexType name="float" >
+    <xs:simpleContent>
+      <xs:extension base="xs:float" >
+        <xs:attributeGroup ref="tns:commonAttributes" />
+      </xs:extension>
+    </xs:simpleContent>
+  </xs:complexType>
+
+  <xs:element name="double" type="tns:double" />
+  <xs:complexType name="double" >
+    <xs:simpleContent>
+      <xs:extension base="xs:double" >
+        <xs:attributeGroup ref="tns:commonAttributes" />
+      </xs:extension>
+    </xs:simpleContent>
+  </xs:complexType>
+
+  <xs:element name="anyURI" type="tns:anyURI" />
+  <xs:complexType name="anyURI" >
+    <xs:simpleContent>
+      <xs:extension base="xs:anyURI" >
+        <xs:attributeGroup ref="tns:commonAttributes" />
+      </xs:extension>
+    </xs:simpleContent>
+  </xs:complexType>
+
+  <xs:element name="QName" type="tns:QName" />
+  <xs:complexType name="QName" >
+    <xs:simpleContent>
+      <xs:extension base="xs:QName" >
+        <xs:attributeGroup ref="tns:commonAttributes" />
+      </xs:extension>
+    </xs:simpleContent>
+  </xs:complexType>
+
+  
+  <xs:element name="string" type="tns:string" />
+  <xs:complexType name="string" >
+    <xs:simpleContent>
+      <xs:extension base="xs:string" >
+        <xs:attributeGroup ref="tns:commonAttributes" />
+      </xs:extension>
+    </xs:simpleContent>
+  </xs:complexType>
+
+  <xs:element name="normalizedString" type="tns:normalizedString" />
+  <xs:complexType name="normalizedString" >
+    <xs:simpleContent>
+      <xs:extension base="xs:normalizedString" >
+        <xs:attributeGroup ref="tns:commonAttributes" />
+      </xs:extension>
+    </xs:simpleContent>
+  </xs:complexType>
+
+  <xs:element name="token" type="tns:token" />
+  <xs:complexType name="token" >
+    <xs:simpleContent>
+      <xs:extension base="xs:token" >
+        <xs:attributeGroup ref="tns:commonAttributes" />
+      </xs:extension>
+    </xs:simpleContent>
+  </xs:complexType>
+
+  <xs:element name="language" type="tns:language" />
+  <xs:complexType name="language" >
+    <xs:simpleContent>
+      <xs:extension base="xs:language" >
+        <xs:attributeGroup ref="tns:commonAttributes" />
+      </xs:extension>
+    </xs:simpleContent>
+  </xs:complexType>
+
+  <xs:element name="Name" type="tns:Name" />
+  <xs:complexType name="Name" >
+    <xs:simpleContent>
+      <xs:extension base="xs:Name" >
+        <xs:attributeGroup ref="tns:commonAttributes" />
+      </xs:extension>
+    </xs:simpleContent>
+  </xs:complexType>
+
+  <xs:element name="NMTOKEN" type="tns:NMTOKEN" />
+  <xs:complexType name="NMTOKEN" >
+    <xs:simpleContent>
+      <xs:extension base="xs:NMTOKEN" >
+        <xs:attributeGroup ref="tns:commonAttributes" />
+      </xs:extension>
+    </xs:simpleContent>
+  </xs:complexType>
+
+  <xs:element name="NCName" type="tns:NCName" />
+  <xs:complexType name="NCName" >
+    <xs:simpleContent>
+      <xs:extension base="xs:NCName" >
+        <xs:attributeGroup ref="tns:commonAttributes" />
+      </xs:extension>
+    </xs:simpleContent>
+  </xs:complexType>
+
+  <xs:element name="NMTOKENS" type="tns:NMTOKENS" />
+  <xs:complexType name="NMTOKENS" >
+    <xs:simpleContent>
+      <xs:extension base="xs:NMTOKENS" >
+        <xs:attributeGroup ref="tns:commonAttributes" />
+      </xs:extension>
+    </xs:simpleContent>
+  </xs:complexType>
+
+  <xs:element name="ID" type="tns:ID" />
+  <xs:complexType name="ID" >
+    <xs:simpleContent>
+      <xs:extension base="xs:ID" >
+        <xs:attributeGroup ref="tns:commonAttributes" />
+      </xs:extension>
+    </xs:simpleContent>
+  </xs:complexType>
+
+  <xs:element name="IDREF" type="tns:IDREF" />
+  <xs:complexType name="IDREF" >
+    <xs:simpleContent>
+      <xs:extension base="xs:IDREF" >
+        <xs:attributeGroup ref="tns:commonAttributes" />
+      </xs:extension>
+    </xs:simpleContent>
+  </xs:complexType>
+
+  <xs:element name="ENTITY" type="tns:ENTITY" />
+  <xs:complexType name="ENTITY" >
+    <xs:simpleContent>
+      <xs:extension base="xs:ENTITY" >
+        <xs:attributeGroup ref="tns:commonAttributes" />
+      </xs:extension>
+    </xs:simpleContent>
+  </xs:complexType>
+
+  <xs:element name="IDREFS" type="tns:IDREFS" />
+  <xs:complexType name="IDREFS" >
+    <xs:simpleContent>
+      <xs:extension base="xs:IDREFS" >
+        <xs:attributeGroup ref="tns:commonAttributes" />
+      </xs:extension>
+    </xs:simpleContent>
+  </xs:complexType>
+
+  <xs:element name="ENTITIES" type="tns:ENTITIES" />
+  <xs:complexType name="ENTITIES" >
+    <xs:simpleContent>
+      <xs:extension base="xs:ENTITIES" >
+        <xs:attributeGroup ref="tns:commonAttributes" />
+      </xs:extension>
+    </xs:simpleContent>
+  </xs:complexType>
+
+  <xs:element name="decimal" type="tns:decimal" />
+  <xs:complexType name="decimal" >
+    <xs:simpleContent>
+      <xs:extension base="xs:decimal" >
+        <xs:attributeGroup ref="tns:commonAttributes" />
+      </xs:extension>
+    </xs:simpleContent>
+  </xs:complexType>
+
+  <xs:element name="integer" type="tns:integer" />
+  <xs:complexType name="integer" >
+    <xs:simpleContent>
+      <xs:extension base="xs:integer" >
+        <xs:attributeGroup ref="tns:commonAttributes" />
+      </xs:extension>
+    </xs:simpleContent>
+  </xs:complexType>
+
+  <xs:element name="nonPositiveInteger" type="tns:nonPositiveInteger" />
+  <xs:complexType name="nonPositiveInteger" >
+    <xs:simpleContent>
+      <xs:extension base="xs:nonPositiveInteger" >
+        <xs:attributeGroup ref="tns:commonAttributes" />
+      </xs:extension>
+    </xs:simpleContent>
+  </xs:complexType>
+
+  <xs:element name="negativeInteger" type="tns:negativeInteger" />
+  <xs:complexType name="negativeInteger" >
+    <xs:simpleContent>
+      <xs:extension base="xs:negativeInteger" >
+        <xs:attributeGroup ref="tns:commonAttributes" />
+      </xs:extension>
+    </xs:simpleContent>
+  </xs:complexType>
+
+  <xs:element name="long" type="tns:long" />
+  <xs:complexType name="long" >
+    <xs:simpleContent>
+      <xs:extension base="xs:long" >
+        <xs:attributeGroup ref="tns:commonAttributes" />
+      </xs:extension>
+    </xs:simpleContent>
+  </xs:complexType>
+
+  <xs:element name="int" type="tns:int" />
+  <xs:complexType name="int" >
+    <xs:simpleContent>
+      <xs:extension base="xs:int" >
+        <xs:attributeGroup ref="tns:commonAttributes" />
+      </xs:extension>
+    </xs:simpleContent>
+  </xs:complexType>
+
+  <xs:element name="short" type="tns:short" />
+  <xs:complexType name="short" >
+    <xs:simpleContent>
+      <xs:extension base="xs:short" >
+        <xs:attributeGroup ref="tns:commonAttributes" />
+      </xs:extension>
+    </xs:simpleContent>
+  </xs:complexType>
+
+  <xs:element name="byte" type="tns:byte" />
+  <xs:complexType name="byte" >
+    <xs:simpleContent>
+      <xs:extension base="xs:byte" >
+        <xs:attributeGroup ref="tns:commonAttributes" />
+      </xs:extension>
+    </xs:simpleContent>
+  </xs:complexType>
+
+  <xs:element name="nonNegativeInteger" type="tns:nonNegativeInteger" />
+  <xs:complexType name="nonNegativeInteger" >
+    <xs:simpleContent>
+      <xs:extension base="xs:nonNegativeInteger" >
+        <xs:attributeGroup ref="tns:commonAttributes" />
+      </xs:extension>
+    </xs:simpleContent>
+  </xs:complexType>
+
+  <xs:element name="unsignedLong" type="tns:unsignedLong" />
+  <xs:complexType name="unsignedLong" >
+    <xs:simpleContent>
+      <xs:extension base="xs:unsignedLong" >
+        <xs:attributeGroup ref="tns:commonAttributes" />
+      </xs:extension>
+    </xs:simpleContent>
+  </xs:complexType>
+
+  <xs:element name="unsignedInt" type="tns:unsignedInt" />
+  <xs:complexType name="unsignedInt" >
+    <xs:simpleContent>
+      <xs:extension base="xs:unsignedInt" >
+        <xs:attributeGroup ref="tns:commonAttributes" />
+      </xs:extension>
+    </xs:simpleContent>
+  </xs:complexType>
+
+  <xs:element name="unsignedShort" type="tns:unsignedShort" />
+  <xs:complexType name="unsignedShort" >
+    <xs:simpleContent>
+      <xs:extension base="xs:unsignedShort" >
+        <xs:attributeGroup ref="tns:commonAttributes" />
+      </xs:extension>
+    </xs:simpleContent>
+  </xs:complexType>
+
+  <xs:element name="unsignedByte" type="tns:unsignedByte" />
+  <xs:complexType name="unsignedByte" >
+    <xs:simpleContent>
+      <xs:extension base="xs:unsignedByte" >
+        <xs:attributeGroup ref="tns:commonAttributes" />
+      </xs:extension>
+    </xs:simpleContent>
+  </xs:complexType>
+
+  <xs:element name="positiveInteger" type="tns:positiveInteger" />
+  <xs:complexType name="positiveInteger" >
+    <xs:simpleContent>
+      <xs:extension base="xs:positiveInteger" >
+        <xs:attributeGroup ref="tns:commonAttributes" />
+      </xs:extension>
+    </xs:simpleContent>
+  </xs:complexType>
+
+  <xs:element name="anyType" />
+</xs:schema>
diff --git a/tests/wsdl_files/soap_import_2.wsdl b/tests/wsdl_files/soap_import_2.wsdl
new file mode 100644
index 0000000..be02d41
--- /dev/null
+++ b/tests/wsdl_files/soap_import_2.wsdl
@@ -0,0 +1,42 @@
+<?xml version="1.0"?>
+<definitions 
+    xmlns:tns="http://example.com/stockquote.wsdl" 
+    xmlns:xsd1="http://example.com/stockquote.xsd" 
+    xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" 
+    xmlns="http://schemas.xmlsoap.org/wsdl/"
+    name="StockQuote" 
+    targetNamespace="http://example.com/stockquote.wsdl">
+  <types>
+    <schema xmlns="http://www.w3.org/2001/XMLSchema" 
+            targetNamespace="http://test.python-zeep.org/imports"
+            xmlns:tns="http://test.python-zeep.org/imports">
+      <import schemaLocation="test_import_2.xsd" namespace="http://example.com/stockquote.xsd"/>
+    </schema>
+    <schema xmlns="http://www.w3.org/2001/XMLSchema" 
+            targetNamespace="http://test.python-zeep.org/something"
+            xmlns:tns="http://test.python-zeep.org/something">
+      <import namespace="http://schemas.xmlsoap.org/soap/encoding/" 
+              schemaLocation="http://schemas.xmlsoap.org/soap/encoding/"/>
+    </schema>
+  </types>
+  <message name="GetLastTradePriceInput">
+    <part name="body" element="xsd1:TradePriceRequest"/>
+  </message>
+  <message name="GetLastTradePriceOutput">
+    <part name="body" element="xsd1:TradePrice"/>
+  </message>
+  <message name="FaultMessageMsg1">
+    <part name="fault1" element="xsd1:Fault1"/>
+  </message>
+  <message name="FaultMessageMsg2">
+    <part name="fault2" element="xsd1:Fault2"/>
+  </message>
+  <portType name="StockQuotePortType">
+    <operation name="GetLastTradePrice">
+      <input message="tns:GetLastTradePriceInput"/>
+      <output message="tns:GetLastTradePriceOutput"/>
+      <fault message="tns:FaultMessageMsg1" name="fault1"/>
+      <fault message="tns:FaultMessageMsg2" name="fault2"/>
+    </operation>
+  </portType>
+</definitions>
diff --git a/tests/wsdl_files/soap_import_main.wsdl b/tests/wsdl_files/soap_import_main.wsdl
new file mode 100644
index 0000000..976c4cf
--- /dev/null
+++ b/tests/wsdl_files/soap_import_main.wsdl
@@ -0,0 +1,38 @@
+<?xml version="1.0"?>
+<definitions 
+    xmlns:tns="http://example.com/stockquote.wsdl" 
+    xmlns:xsd1="http://example.com/stockquote.xsd" 
+    xmlns:sub="http://test.python-zeep.org/sub"
+    xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" 
+    xmlns="http://schemas.xmlsoap.org/wsdl/"
+    name="StockQuote" 
+    targetNamespace="http://example.com/stockquote.wsdl">
+  <import namespace="http://test.python-zeep.org/sub" location="soap_import_2.wsdl"/>
+
+  <types/>
+  <binding name="StockQuoteBinding" type="tns:StockQuotePortType">
+    <soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http"/>
+    <operation name="GetLastTradePrice">
+      <soap:operation soapAction="http://example.com/GetLastTradePrice"/>
+      <input>
+        <soap:body use="literal"/>
+      </input>
+      <output>
+        <soap:body use="literal"/>
+      </output>
+      <fault name="fault1">
+        <soap:fault name="fault1" use="literal"/>
+      </fault>
+      <fault name="fault2">
+        <soap:fault name="fault2" use="literal"/>
+      </fault>
+    </operation>
+  </binding>
+  <service name="StockQuoteService">
+    <documentation>My first service</documentation>
+    <port name="StockQuotePort" binding="tns:StockQuoteBinding">
+      <soap:address location="http://example.com/stockquote"/>
+    </port>
+  </service>
+</definitions>
+
diff --git a/tests/wsdl_files/soap_transport_err.wsdl b/tests/wsdl_files/soap_transport_err.wsdl
new file mode 100644
index 0000000..980f632
--- /dev/null
+++ b/tests/wsdl_files/soap_transport_err.wsdl
@@ -0,0 +1,138 @@
+<?xml version="1.0"?>
+<definitions 
+    xmlns:tns="http://example.com/stockquote.wsdl" 
+    xmlns:xsd1="http://example.com/stockquote.xsd" 
+    xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" 
+    xmlns="http://schemas.xmlsoap.org/wsdl/"
+    name="StockQuote" 
+    targetNamespace="http://example.com/stockquote.wsdl">
+  <types>
+    <schema xmlns="http://www.w3.org/2001/XMLSchema" 
+            targetNamespace="http://example.com/stockquote.xsd"
+            xmlns:tns="http://example.com/stockquote.xsd" >
+      <complexType name="Address">
+        <sequence>
+          <element minOccurs="0" maxOccurs="1" name="NameFirst" type="string"/>
+          <element minOccurs="0" maxOccurs="1" name="NameLast" type="string"/>
+          <element minOccurs="0" maxOccurs="1" name="Email" type="string"/>
+        </sequence>
+      </complexType>
+      <element name="Fault1">
+        <complexType>
+          <sequence>
+            <element name="message" type="string"/>
+          </sequence>
+        </complexType>
+      </element>
+      <element name="Fault2">
+        <complexType>
+          <sequence>
+            <element name="message" type="string"/>
+          </sequence>
+        </complexType>
+      </element>
+      <element name="TradePriceRequest">
+        <complexType>
+          <all>
+            <element name="tickerSymbol" type="string"/>
+            <element name="account" type="tns:account" minOccurs="0" />
+            <element ref="tns:country"/>
+          </all>
+        </complexType>
+      </element>
+      <element name="TradePrice">
+        <complexType>
+          <all>
+            <element name="price" type="float"/>
+          </all>
+        </complexType>
+      </element>
+      <complexType name="account">
+        <sequence>
+          <element name="id" type="int"/>
+          <element name="user" type="string"/>
+        </sequence>
+      </complexType>
+      <complexType name="country">
+          <sequence>
+            <element name="code" type="string"/>
+          </sequence>
+      </complexType>
+      <element name="country">
+        <complexType>
+          <sequence>
+            <element name="name" type="string"/>
+            <element name="code" type="string"/>
+          </sequence>
+        </complexType>
+      </element>
+    </schema>
+  </types>
+  <message name="GetLastTradePriceInput">
+    <part name="body" element="xsd1:TradePriceRequest"/>
+  </message>
+  <message name="GetLastTradePriceOutput">
+    <part name="body" element="xsd1:TradePrice"/>
+  </message>
+  <message name="FaultMessageMsg1">
+    <part name="fault1" element="xsd1:Fault1"/>
+  </message>
+  <message name="FaultMessageMsg2">
+    <part name="fault2" element="xsd1:Fault2"/>
+  </message>
+  <portType name="StockQuotePortType">
+    <operation name="GetLastTradePrice">
+      <input message="tns:GetLastTradePriceInput"/>
+      <output message="tns:GetLastTradePriceOutput"/>
+      <fault message="tns:FaultMessageMsg1" name="fault1"/>
+      <fault message="tns:FaultMessageMsg2" name="fault2"/>
+    </operation>
+  </portType>
+
+  <binding name="StockQuoteBinding" type="tns:StockQuotePortType">
+    <soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http"/>
+    <operation name="GetLastTradePrice">
+      <soap:operation soapAction="http://example.com/GetLastTradePrice"/>
+      <input>
+        <soap:body use="literal"/>
+      </input>
+      <output>
+        <soap:body use="literal"/>
+      </output>
+      <fault name="fault1">
+        <soap:fault name="fault1" use="literal"/>
+      </fault>
+      <fault name="fault2">
+        <soap:fault name="fault2" use="literal"/>
+      </fault>
+    </operation>
+  </binding>
+
+  <binding name="StockQuoteBinding" type="tns:StockQuotePortType">
+	  <soap:binding style="document" transport="http://schemas.microsoft.com/soap/tcp"/>
+    <operation name="GetLastTradePrice">
+      <soap:operation soapAction="http://example.com/GetLastTradePrice"/>
+      <input>
+        <soap:body use="literal"/>
+      </input>
+      <output>
+        <soap:body use="literal"/>
+      </output>
+      <fault name="fault1">
+        <soap:fault name="fault1" use="literal"/>
+      </fault>
+      <fault name="fault2">
+        <soap:fault name="fault2" use="literal"/>
+      </fault>
+    </operation>
+  </binding>
+
+
+
+  <service name="StockQuoteService">
+    <documentation>My first service</documentation>
+    <port name="StockQuotePort" binding="tns:StockQuoteBinding">
+      <soap:address location="http://example.com/stockquote"/>
+    </port>
+  </service>
+</definitions>
diff --git a/tests/wsdl_files/test_import_2.xsd b/tests/wsdl_files/test_import_2.xsd
new file mode 100644
index 0000000..90194f6
--- /dev/null
+++ b/tests/wsdl_files/test_import_2.xsd
@@ -0,0 +1,60 @@
+<schema xmlns="http://www.w3.org/2001/XMLSchema" 
+        targetNamespace="http://example.com/stockquote.xsd"
+        xmlns:tns="http://example.com/stockquote.xsd" >
+  <complexType name="Address">
+    <sequence>
+      <element minOccurs="0" maxOccurs="1" name="NameFirst" type="string"/>
+      <element minOccurs="0" maxOccurs="1" name="NameLast" type="string"/>
+      <element minOccurs="0" maxOccurs="1" name="Email" type="string"/>
+    </sequence>
+  </complexType>
+  <element name="Fault1">
+    <complexType>
+      <sequence>
+        <element name="message" type="string"/>
+      </sequence>
+    </complexType>
+  </element>
+  <element name="Fault2">
+    <complexType>
+      <sequence>
+        <element name="message" type="string"/>
+      </sequence>
+    </complexType>
+  </element>
+  <element name="TradePriceRequest">
+    <complexType>
+      <all>
+        <element name="tickerSymbol" type="string"/>
+        <element name="account" type="tns:account" minOccurs="0" />
+        <element ref="tns:country"/>
+      </all>
+    </complexType>
+  </element>
+  <element name="TradePrice">
+    <complexType>
+      <all>
+        <element name="price" type="float"/>
+      </all>
+    </complexType>
+  </element>
+  <complexType name="account">
+    <sequence>
+      <element name="id" type="int"/>
+      <element name="user" type="string"/>
+    </sequence>
+  </complexType>
+  <complexType name="country">
+      <sequence>
+        <element name="code" type="string"/>
+      </sequence>
+  </complexType>
+  <element name="country">
+    <complexType>
+      <sequence>
+        <element name="name" type="string"/>
+        <element name="code" type="string"/>
+      </sequence>
+    </complexType>
+  </element>
+</schema>
diff --git a/tests/wsdl_files/xmldsig-core-schema.xsd b/tests/wsdl_files/xmldsig-core-schema.xsd
new file mode 100644
index 0000000..dd5254b
--- /dev/null
+++ b/tests/wsdl_files/xmldsig-core-schema.xsd
@@ -0,0 +1,309 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!-- Schema for XML Signatures
+    http://www.w3.org/2000/09/xmldsig#
+    $Revision: 1.1 $ on $Date: 2002/02/08 20:32:26 $ by $Author: reagle $
+
+    Copyright 2001 The Internet Society and W3C (Massachusetts Institute
+    of Technology, Institut National de Recherche en Informatique et en
+    Automatique, Keio University). All Rights Reserved.
+    http://www.w3.org/Consortium/Legal/
+
+    This document is governed by the W3C Software License [1] as described
+    in the FAQ [2].
+
+    [1] http://www.w3.org/Consortium/Legal/copyright-software-19980720
+    [2] http://www.w3.org/Consortium/Legal/IPR-FAQ-20000620.html#DTD
+-->
+
+
+<schema xmlns="http://www.w3.org/2001/XMLSchema"
+        xmlns:ds="http://www.w3.org/2000/09/xmldsig#"
+        targetNamespace="http://www.w3.org/2000/09/xmldsig#"
+        version="0.1" elementFormDefault="qualified"> 
+
+<!-- Basic Types Defined for Signatures -->
+
+<simpleType name="CryptoBinary">
+  <restriction base="base64Binary">
+  </restriction>
+</simpleType>
+
+<!-- Start Signature -->
+
+<element name="Signature" type="ds:SignatureType"/>
+<complexType name="SignatureType">
+  <sequence> 
+    <element ref="ds:SignedInfo"/> 
+    <element ref="ds:SignatureValue"/> 
+    <element ref="ds:KeyInfo" minOccurs="0"/> 
+    <element ref="ds:Object" minOccurs="0" maxOccurs="unbounded"/> 
+  </sequence>  
+  <attribute name="Id" type="ID" use="optional"/>
+</complexType>
+
+  <element name="SignatureValue" type="ds:SignatureValueType"/> 
+  <complexType name="SignatureValueType">
+    <simpleContent>
+      <extension base="base64Binary">
+        <attribute name="Id" type="ID" use="optional"/>
+      </extension>
+    </simpleContent>
+  </complexType>
+
+<!-- Start SignedInfo -->
+
+<element name="SignedInfo" type="ds:SignedInfoType"/>
+<complexType name="SignedInfoType">
+  <sequence> 
+    <element ref="ds:CanonicalizationMethod"/> 
+    <element ref="ds:SignatureMethod"/> 
+    <element ref="ds:Reference" maxOccurs="unbounded"/> 
+  </sequence>  
+  <attribute name="Id" type="ID" use="optional"/> 
+</complexType>
+
+  <element name="CanonicalizationMethod" type="ds:CanonicalizationMethodType"/> 
+  <complexType name="CanonicalizationMethodType" mixed="true">
+    <sequence>
+      <any namespace="##any" minOccurs="0" maxOccurs="unbounded"/>
+      <!-- (0,unbounded) elements from (1,1) namespace -->
+    </sequence>
+    <attribute name="Algorithm" type="anyURI" use="required"/> 
+  </complexType>
+
+  <element name="SignatureMethod" type="ds:SignatureMethodType"/>
+  <complexType name="SignatureMethodType" mixed="true">
+    <sequence>
+      <element name="HMACOutputLength" minOccurs="0" type="ds:HMACOutputLengthType"/>
+      <any namespace="##other" minOccurs="0" maxOccurs="unbounded"/>
+      <!-- (0,unbounded) elements from (1,1) external namespace -->
+    </sequence>
+    <attribute name="Algorithm" type="anyURI" use="required"/> 
+  </complexType>
+
+<!-- Start Reference -->
+
+<element name="Reference" type="ds:ReferenceType"/>
+<complexType name="ReferenceType">
+  <sequence> 
+    <element ref="ds:Transforms" minOccurs="0"/> 
+    <element ref="ds:DigestMethod"/> 
+    <element ref="ds:DigestValue"/> 
+  </sequence>
+  <attribute name="Id" type="ID" use="optional"/> 
+  <attribute name="URI" type="anyURI" use="optional"/> 
+  <attribute name="Type" type="anyURI" use="optional"/> 
+</complexType>
+
+  <element name="Transforms" type="ds:TransformsType"/>
+  <complexType name="TransformsType">
+    <sequence>
+      <element ref="ds:Transform" maxOccurs="unbounded"/>  
+    </sequence>
+  </complexType>
+
+  <element name="Transform" type="ds:TransformType"/>
+  <complexType name="TransformType" mixed="true">
+    <choice minOccurs="0" maxOccurs="unbounded"> 
+      <any namespace="##other" processContents="lax"/>
+      <!-- (1,1) elements from (0,unbounded) namespaces -->
+      <element name="XPath" type="string"/> 
+    </choice>
+    <attribute name="Algorithm" type="anyURI" use="required"/> 
+  </complexType>
+
+<!-- End Reference -->
+
+<element name="DigestMethod" type="ds:DigestMethodType"/>
+<complexType name="DigestMethodType" mixed="true"> 
+  <sequence>
+    <any namespace="##other" processContents="lax" minOccurs="0" maxOccurs="unbounded"/>
+  </sequence>    
+  <attribute name="Algorithm" type="anyURI" use="required"/> 
+</complexType>
+
+<element name="DigestValue" type="ds:DigestValueType"/>
+<simpleType name="DigestValueType">
+  <restriction base="base64Binary"/>
+</simpleType>
+
+<!-- End SignedInfo -->
+
+<!-- Start KeyInfo -->
+
+<element name="KeyInfo" type="ds:KeyInfoType"/> 
+<complexType name="KeyInfoType" mixed="true">
+  <choice maxOccurs="unbounded">     
+    <element ref="ds:KeyName"/> 
+    <element ref="ds:KeyValue"/> 
+    <element ref="ds:RetrievalMethod"/> 
+    <element ref="ds:X509Data"/> 
+    <element ref="ds:PGPData"/> 
+    <element ref="ds:SPKIData"/>
+    <element ref="ds:MgmtData"/>
+    <any processContents="lax" namespace="##other"/>
+    <!-- (1,1) elements from (0,unbounded) namespaces -->
+  </choice>
+  <attribute name="Id" type="ID" use="optional"/> 
+</complexType>
+
+  <element name="KeyName" type="string"/>
+  <element name="MgmtData" type="string"/>
+
+  <element name="KeyValue" type="ds:KeyValueType"/> 
+  <complexType name="KeyValueType" mixed="true">
+   <choice>
+     <element ref="ds:DSAKeyValue"/>
+     <element ref="ds:RSAKeyValue"/>
+     <any namespace="##other" processContents="lax"/>
+   </choice>
+  </complexType>
+
+  <element name="RetrievalMethod" type="ds:RetrievalMethodType"/> 
+  <complexType name="RetrievalMethodType">
+    <sequence>
+      <element ref="ds:Transforms" minOccurs="0"/> 
+    </sequence>  
+    <attribute name="URI" type="anyURI"/>
+    <attribute name="Type" type="anyURI" use="optional"/>
+  </complexType>
+
+<!-- Start X509Data -->
+
+<element name="X509Data" type="ds:X509DataType"/> 
+<complexType name="X509DataType">
+  <sequence maxOccurs="unbounded">
+    <choice>
+      <element name="X509IssuerSerial" type="ds:X509IssuerSerialType"/>
+      <element name="X509SKI" type="base64Binary"/>
+      <element name="X509SubjectName" type="string"/>
+      <element name="X509Certificate" type="base64Binary"/>
+      <element name="X509CRL" type="base64Binary"/>
+      <any namespace="##other" processContents="lax"/>
+    </choice>
+  </sequence>
+</complexType>
+
+<complexType name="X509IssuerSerialType"> 
+  <sequence> 
+    <element name="X509IssuerName" type="string"/> 
+    <element name="X509SerialNumber" type="integer"/> 
+  </sequence>
+</complexType>
+
+<!-- End X509Data -->
+
+<!-- Begin PGPData -->
+
+<element name="PGPData" type="ds:PGPDataType"/> 
+<complexType name="PGPDataType"> 
+  <choice>
+    <sequence>
+      <element name="PGPKeyID" type="base64Binary"/> 
+      <element name="PGPKeyPacket" type="base64Binary" minOccurs="0"/> 
+      <any namespace="##other" processContents="lax" minOccurs="0"
+       maxOccurs="unbounded"/>
+    </sequence>
+    <sequence>
+      <element name="PGPKeyPacket" type="base64Binary"/> 
+      <any namespace="##other" processContents="lax" minOccurs="0"
+       maxOccurs="unbounded"/>
+    </sequence>
+  </choice>
+</complexType>
+
+<!-- End PGPData -->
+
+<!-- Begin SPKIData -->
+
+<element name="SPKIData" type="ds:SPKIDataType"/> 
+<complexType name="SPKIDataType">
+  <sequence maxOccurs="unbounded">
+    <element name="SPKISexp" type="base64Binary"/>
+    <any namespace="##other" processContents="lax" minOccurs="0"/>
+  </sequence>
+</complexType> 
+
+<!-- End SPKIData -->
+
+<!-- End KeyInfo -->
+
+<!-- Start Object (Manifest, SignatureProperty) -->
+
+<element name="Object" type="ds:ObjectType"/> 
+<complexType name="ObjectType" mixed="true">
+  <sequence minOccurs="0" maxOccurs="unbounded">
+    <any namespace="##any" processContents="lax"/>
+  </sequence>
+  <attribute name="Id" type="ID" use="optional"/> 
+  <attribute name="MimeType" type="string" use="optional"/> <!-- add a grep facet -->
+  <attribute name="Encoding" type="anyURI" use="optional"/> 
+</complexType>
+
+<element name="Manifest" type="ds:ManifestType"/> 
+<complexType name="ManifestType">
+  <sequence>
+    <element ref="ds:Reference" maxOccurs="unbounded"/> 
+  </sequence>
+  <attribute name="Id" type="ID" use="optional"/> 
+</complexType>
+
+<element name="SignatureProperties" type="ds:SignaturePropertiesType"/> 
+<complexType name="SignaturePropertiesType">
+  <sequence>
+    <element ref="ds:SignatureProperty" maxOccurs="unbounded"/> 
+  </sequence>
+  <attribute name="Id" type="ID" use="optional"/> 
+</complexType>
+
+   <element name="SignatureProperty" type="ds:SignaturePropertyType"/> 
+   <complexType name="SignaturePropertyType" mixed="true">
+     <choice maxOccurs="unbounded">
+       <any namespace="##other" processContents="lax"/>
+       <!-- (1,1) elements from (1,unbounded) namespaces -->
+     </choice>
+     <attribute name="Target" type="anyURI" use="required"/> 
+     <attribute name="Id" type="ID" use="optional"/> 
+   </complexType>
+
+<!-- End Object (Manifest, SignatureProperty) -->
+
+<!-- Start Algorithm Parameters -->
+
+<simpleType name="HMACOutputLengthType">
+  <restriction base="integer"/>
+</simpleType>
+
+<!-- Start KeyValue Element-types -->
+
+<element name="DSAKeyValue" type="ds:DSAKeyValueType"/>
+<complexType name="DSAKeyValueType">
+  <sequence>
+    <sequence minOccurs="0">
+      <element name="P" type="ds:CryptoBinary"/>
+      <element name="Q" type="ds:CryptoBinary"/>
+    </sequence>
+    <element name="G" type="ds:CryptoBinary" minOccurs="0"/>
+    <element name="Y" type="ds:CryptoBinary"/>
+    <element name="J" type="ds:CryptoBinary" minOccurs="0"/>
+    <sequence minOccurs="0">
+      <element name="Seed" type="ds:CryptoBinary"/>
+      <element name="PgenCounter" type="ds:CryptoBinary"/>
+    </sequence>
+  </sequence>
+</complexType>
+
+<element name="RSAKeyValue" type="ds:RSAKeyValueType"/>
+<complexType name="RSAKeyValueType">
+  <sequence>
+    <element name="Modulus" type="ds:CryptoBinary"/> 
+    <element name="Exponent" type="ds:CryptoBinary"/> 
+  </sequence>
+</complexType> 
+
+<!-- End KeyValue Element-types -->
+
+<!-- End Signature -->
+
+</schema>
diff --git a/tox.ini b/tox.ini
deleted file mode 100644
index be542d5..0000000
--- a/tox.ini
+++ /dev/null
@@ -1,7 +0,0 @@
-[tox]
-envlist = py27,py33,py34,py35,pypy
-
-[testenv]
-commands = 
-    pip install .[test]
-    py.test -vvv
-- 
python-zeep



More information about the tryton-debian-vcs mailing list