Skip to content

Commit aa2ba63

Browse files
committed
Add support for nested xsd:choice elements (mvantellingen#370)
1 parent 528d03a commit aa2ba63

File tree

3 files changed

+112
-8
lines changed

3 files changed

+112
-8
lines changed

CHANGES

+5
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1+
1.3.0 (unreleased)
2+
------------------
3+
- Add support for nested xsd:choice elements (#370)
4+
5+
16
1.2.0 (2017-03-12)
27
------------------
38
- Add flag to disable strict mode in the Client. This allows zeep to better

src/zeep/xsd/elements/indicators.py

+51-8
Original file line numberDiff line numberDiff line change
@@ -226,6 +226,19 @@ class All(OrderIndicator):
226226
"""
227227

228228
def parse_xmlelements(self, xmlelements, schema, name=None, context=None):
229+
"""Consume matching xmlelements
230+
231+
:param xmlelements: Dequeue of XML element objects
232+
:type xmlelements: collections.deque of lxml.etree._Element
233+
:param schema: The parent XML schema
234+
:type schema: zeep.xsd.Schema
235+
:param name: The name of the parent element
236+
:type name: str
237+
:param context: Optional parsing context (for inline schemas)
238+
:type context: zeep.xsd.context.XmlParserContext
239+
:rtype: dict or None
240+
241+
"""
229242
result = OrderedDict()
230243
expected_tags = {element.qname for __, element in self.elements}
231244
consumed_tags = set()
@@ -261,7 +274,19 @@ def default_value(self):
261274
return OrderedDict()
262275

263276
def parse_xmlelements(self, xmlelements, schema, name=None, context=None):
264-
"""Return a dictionary"""
277+
"""Consume matching xmlelements
278+
279+
:param xmlelements: Dequeue of XML element objects
280+
:type xmlelements: collections.deque of lxml.etree._Element
281+
:param schema: The parent XML schema
282+
:type schema: zeep.xsd.Schema
283+
:param name: The name of the parent element
284+
:type name: str
285+
:param context: Optional parsing context (for inline schemas)
286+
:type context: zeep.xsd.context.XmlParserContext
287+
:rtype: dict or None
288+
289+
"""
265290
result = []
266291

267292
for _unused in max_occurs_iter(self.max_occurs):
@@ -319,7 +344,7 @@ def parse_kwargs(self, kwargs, name, available_kwargs):
319344
This handles two distinct initialization methods:
320345
321346
1. Passing the choice elements directly to the kwargs (unnested)
322-
2. Passing the choice elements into the `name` kwarg (_alue_1) (nested).
347+
2. Passing the choice elements into the `name` kwarg (_value_1) (nested).
323348
This case is required when multiple choice elements are given.
324349
325350
:param name: Name of the choice element (_value_1)
@@ -369,9 +394,9 @@ def parse_kwargs(self, kwargs, name, available_kwargs):
369394

370395
# When choice elements are specified directly in the kwargs
371396
found = False
372-
for i, choice in enumerate(self):
397+
for name, choice in self.elements_nested:
373398
temp_kwargs = copy.copy(available_kwargs)
374-
subresult = choice.parse_kwargs(kwargs, None, temp_kwargs)
399+
subresult = choice.parse_kwargs(kwargs, name, temp_kwargs)
375400

376401
if subresult:
377402
if not any(subresult.values()):
@@ -416,8 +441,12 @@ def accept(self, values):
416441
nums = set()
417442
for name, element in self.elements_nested:
418443
if isinstance(element, Element):
419-
if name in values and values[name]:
420-
nums.add(1)
444+
if self.accepts_multiple:
445+
if all(name in item and item[name] for item in values):
446+
nums.add(1)
447+
else:
448+
if name in values and values[name]:
449+
nums.add(1)
421450
else:
422451
num = element.accept(values)
423452
nums.add(num)
@@ -473,7 +502,8 @@ class Sequence(OrderIndicator):
473502
474503
"""
475504
def parse_xmlelements(self, xmlelements, schema, name=None, context=None):
476-
"""
505+
"""Consume matching xmlelements
506+
477507
:param xmlelements: Dequeue of XML element objects
478508
:type xmlelements: collections.deque of lxml.etree._Element
479509
:param schema: The parent XML schema
@@ -482,7 +512,7 @@ def parse_xmlelements(self, xmlelements, schema, name=None, context=None):
482512
:type name: str
483513
:param context: Optional parsing context (for inline schemas)
484514
:type context: zeep.xsd.context.XmlParserContext
485-
:return: dict or None
515+
:rtype: dict or None
486516
487517
"""
488518
result = []
@@ -585,6 +615,19 @@ def parse_kwargs(self, kwargs, name, available_kwargs):
585615
return result
586616

587617
def parse_xmlelements(self, xmlelements, schema, name=None, context=None):
618+
"""Consume matching xmlelements
619+
620+
:param xmlelements: Dequeue of XML element objects
621+
:type xmlelements: collections.deque of lxml.etree._Element
622+
:param schema: The parent XML schema
623+
:type schema: zeep.xsd.Schema
624+
:param name: The name of the parent element
625+
:type name: str
626+
:param context: Optional parsing context (for inline schemas)
627+
:type context: zeep.xsd.context.XmlParserContext
628+
:rtype: dict or None
629+
630+
"""
588631
result = []
589632

590633
for _unused in max_occurs_iter(self.max_occurs):

tests/test_xsd_indicators_choice.py

+56
Original file line numberDiff line numberDiff line change
@@ -1073,3 +1073,59 @@ def test_choice_extend():
10731073
assert value['item-1-2'] == 'bar'
10741074
assert value['_value_1'][0] == {'item-2-1': 'xafoo'}
10751075
assert value['_value_1'][1] == {'item-2-2': 'xabar'}
1076+
1077+
1078+
def test_nested_choice():
1079+
schema = xsd.Schema(load_xml("""
1080+
<?xml version="1.0"?>
1081+
<schema xmlns="http://www.w3.org/2001/XMLSchema"
1082+
xmlns:tns="http://tests.python-zeep.org/"
1083+
targetNamespace="http://tests.python-zeep.org/"
1084+
elementFormDefault="qualified">
1085+
<element name="container">
1086+
<complexType>
1087+
<sequence>
1088+
<choice>
1089+
<choice minOccurs="2" maxOccurs="unbounded">
1090+
<element ref="tns:a" />
1091+
</choice>
1092+
<element ref="tns:b" />
1093+
</choice>
1094+
</sequence>
1095+
</complexType>
1096+
</element>
1097+
<element name="a" type="string" />
1098+
<element name="b" type="string" />
1099+
</schema>
1100+
"""))
1101+
1102+
schema.set_ns_prefix('tns', 'http://tests.python-zeep.org/')
1103+
container_type = schema.get_element('tns:container')
1104+
1105+
item = container_type(_value_1=[{'a': 'item-1'}, {'a': 'item-2'}])
1106+
assert item._value_1[0] == {'a': 'item-1'}
1107+
assert item._value_1[1] == {'a': 'item-2'}
1108+
1109+
expected = load_xml("""
1110+
<document>
1111+
<ns0:container xmlns:ns0="http://tests.python-zeep.org/">
1112+
<ns0:a>item-1</ns0:a>
1113+
<ns0:a>item-2</ns0:a>
1114+
</ns0:container>
1115+
</document>
1116+
""")
1117+
node = render_node(container_type, item)
1118+
assert_nodes_equal(node, expected)
1119+
1120+
result = container_type.parse(expected[0], schema)
1121+
assert result._value_1[0] == {'a': 'item-1'}
1122+
assert result._value_1[1] == {'a': 'item-2'}
1123+
1124+
expected = load_xml("""
1125+
<container xmlns="http://tests.python-zeep.org/">
1126+
<b>1</b>
1127+
</container>
1128+
""")
1129+
1130+
result = container_type.parse(expected, schema)
1131+
assert result.b == '1'

0 commit comments

Comments
 (0)