Skip to content

Commit 0534694

Browse files
authored
Extract ns from internaltype when available (#206)
* Extract ns from internaltype when available * Handle namespace better * Namespace more tests * Array and namespaces * Fix encoding trouble in the test
1 parent 776744c commit 0534694

File tree

2 files changed

+77
-11
lines changed

2 files changed

+77
-11
lines changed

msrest/serialization.py

Lines changed: 27 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1188,6 +1188,24 @@ def attribute_key_case_insensitive_extractor(attr, _, data):
11881188

11891189
return data.get(found_key)
11901190

1191+
def _extract_name_from_internal_type(internal_type):
1192+
"""Given an internal type XML description, extract correct XML name with namespace.
1193+
1194+
:param dict internal_type: An model type
1195+
:rtype: tuple
1196+
:returns: A tuple XML name + namespace dict
1197+
"""
1198+
internal_type_xml_map = getattr(internal_type, "_xml_map", {})
1199+
xml_name = internal_type_xml_map.get('name', internal_type.__name__)
1200+
xml_ns = internal_type_xml_map.get("ns", None)
1201+
if xml_ns:
1202+
ns = {'prefix': xml_ns}
1203+
xml_name = "prefix:"+xml_name
1204+
else:
1205+
ns = {} # And keep same xml_name
1206+
return xml_name, ns
1207+
1208+
11911209
def xml_key_extractor(attr, attr_desc, data):
11921210
if isinstance(data, dict):
11931211
return None
@@ -1198,41 +1216,39 @@ def xml_key_extractor(attr, attr_desc, data):
11981216

11991217
xml_desc = attr_desc.get('xml', {})
12001218
xml_name = xml_desc.get('name', attr_desc['key'])
1201-
xml_ns = xml_desc.get('ns', None)
12021219

12031220
# If it's an attribute, that's simple
12041221
if xml_desc.get("attr", False):
12051222
return data.get(xml_name)
12061223

1224+
# Look for a children
1225+
is_iter_type = attr_desc['type'].startswith("[")
1226+
is_wrapped = xml_desc.get("wrapped", False)
1227+
internal_type = attr_desc.get("internalType", None)
1228+
internal_type_xml_map = getattr(internal_type, "_xml_map", {})
1229+
12071230
# Integrate namespace if necessary
1231+
xml_ns = xml_desc.get('ns', internal_type_xml_map.get("ns", None))
12081232
if xml_ns:
12091233
ns = {'prefix': xml_ns}
12101234
xml_name = "prefix:"+xml_name
12111235
else:
12121236
ns = {} # And keep same xml_name
12131237

1214-
# Look for a children
1215-
is_iter_type = attr_desc['type'].startswith("[")
1216-
is_wrapped = xml_desc.get("wrapped", False)
1217-
internal_type = attr_desc.get("internalType", None)
1218-
12191238
# Scenario where I take the local name:
12201239
# - Wrapped node
12211240
# - Internal type is an enum (considered basic types)
12221241
# - Internal type has no XML/Name node
1223-
internal_type_xml_map = getattr(internal_type, "_xml_map", {})
12241242
if is_wrapped or (internal_type and (issubclass(internal_type, Enum) or 'name' not in internal_type_xml_map)):
12251243
children = data.findall(xml_name, ns)
12261244
# If internal type has a local name and it's not a list, I use that name
12271245
elif not is_iter_type and internal_type and 'name' in internal_type_xml_map:
1228-
xml_name = internal_type_xml_map["name"]
1229-
ns = internal_type_xml_map.get("ns", None)
1246+
xml_name, ns = _extract_name_from_internal_type(internal_type)
12301247
children = data.findall(xml_name, ns)
12311248
# That's an array
12321249
else:
12331250
if internal_type: # Complex type, ignore itemsName and use the complex type name
1234-
items_name = internal_type_xml_map.get('name', internal_type.__name__)
1235-
ns = internal_type_xml_map.get("ns", None)
1251+
items_name, ns = _extract_name_from_internal_type(internal_type)
12361252
else:
12371253
items_name = xml_desc.get("itemsName", xml_name)
12381254
children = data.findall(items_name, ns)

tests/test_xml_serialization.py

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -471,6 +471,56 @@ class XmlModel(Model):
471471

472472
assert result.age == 37
473473

474+
def test_complex_namespace(self):
475+
"""Test recursive namespace."""
476+
basic_xml = """<?xml version="1.0"?>
477+
<entry xmlns="http://www.w3.org/2005/Atom">
478+
<author>
479+
<name>lmazuel</name>
480+
</author>
481+
<AuthorizationRules xmlns="http://schemas.microsoft.com/netservices/2010/10/servicebus/connect">
482+
<AuthorizationRule>
483+
<KeyName>testpolicy</KeyName>
484+
</AuthorizationRule>
485+
</AuthorizationRules>
486+
</entry>"""
487+
488+
class XmlRoot(Model):
489+
_attribute_map = {
490+
'author': {'key': 'author', 'type': 'QueueDescriptionResponseAuthor'},
491+
'authorization_rules': {'key': 'AuthorizationRules', 'type': '[AuthorizationRule]', 'xml': {'ns': 'http://schemas.microsoft.com/netservices/2010/10/servicebus/connect', 'wrapped': True, 'itemsNs': 'http://schemas.microsoft.com/netservices/2010/10/servicebus/connect'}},
492+
}
493+
_xml_map = {
494+
'name': 'entry', 'ns': 'http://www.w3.org/2005/Atom'
495+
}
496+
497+
class QueueDescriptionResponseAuthor(Model):
498+
_attribute_map = {
499+
'name': {'key': 'name', 'type': 'str', 'xml': {'ns': 'http://www.w3.org/2005/Atom'}},
500+
}
501+
_xml_map = {
502+
'ns': 'http://www.w3.org/2005/Atom'
503+
}
504+
505+
class AuthorizationRule(Model):
506+
_attribute_map = {
507+
'key_name': {'key': 'KeyName', 'type': 'str', 'xml': {'ns': 'http://schemas.microsoft.com/netservices/2010/10/servicebus/connect'}},
508+
}
509+
_xml_map = {
510+
'ns': 'http://schemas.microsoft.com/netservices/2010/10/servicebus/connect'
511+
}
512+
513+
514+
s = Deserializer({
515+
"XmlRoot": XmlRoot,
516+
"QueueDescriptionResponseAuthor": QueueDescriptionResponseAuthor,
517+
"AuthorizationRule": AuthorizationRule,
518+
})
519+
result = s(XmlRoot, basic_xml, "application/xml")
520+
521+
assert result.author.name == "lmazuel"
522+
assert result.authorization_rules[0].key_name == "testpolicy"
523+
474524

475525
class TestXmlSerialization:
476526

0 commit comments

Comments
 (0)