Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 27 additions & 11 deletions msrest/serialization.py
Original file line number Diff line number Diff line change
Expand Up @@ -1188,6 +1188,24 @@ def attribute_key_case_insensitive_extractor(attr, _, data):

return data.get(found_key)

def _extract_name_from_internal_type(internal_type):
"""Given an internal type XML description, extract correct XML name with namespace.

:param dict internal_type: An model type
:rtype: tuple
:returns: A tuple XML name + namespace dict
"""
internal_type_xml_map = getattr(internal_type, "_xml_map", {})
xml_name = internal_type_xml_map.get('name', internal_type.__name__)
xml_ns = internal_type_xml_map.get("ns", None)
if xml_ns:
ns = {'prefix': xml_ns}
xml_name = "prefix:"+xml_name
else:
ns = {} # And keep same xml_name
return xml_name, ns


def xml_key_extractor(attr, attr_desc, data):
if isinstance(data, dict):
return None
Expand All @@ -1198,41 +1216,39 @@ def xml_key_extractor(attr, attr_desc, data):

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

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

# Look for a children
is_iter_type = attr_desc['type'].startswith("[")
is_wrapped = xml_desc.get("wrapped", False)
internal_type = attr_desc.get("internalType", None)
internal_type_xml_map = getattr(internal_type, "_xml_map", {})

# Integrate namespace if necessary
xml_ns = xml_desc.get('ns', internal_type_xml_map.get("ns", None))
if xml_ns:
ns = {'prefix': xml_ns}
xml_name = "prefix:"+xml_name
else:
ns = {} # And keep same xml_name

# Look for a children
is_iter_type = attr_desc['type'].startswith("[")
is_wrapped = xml_desc.get("wrapped", False)
internal_type = attr_desc.get("internalType", None)

# Scenario where I take the local name:
# - Wrapped node
# - Internal type is an enum (considered basic types)
# - Internal type has no XML/Name node
internal_type_xml_map = getattr(internal_type, "_xml_map", {})
if is_wrapped or (internal_type and (issubclass(internal_type, Enum) or 'name' not in internal_type_xml_map)):
children = data.findall(xml_name, ns)
# If internal type has a local name and it's not a list, I use that name
elif not is_iter_type and internal_type and 'name' in internal_type_xml_map:
xml_name = internal_type_xml_map["name"]
ns = internal_type_xml_map.get("ns", None)
xml_name, ns = _extract_name_from_internal_type(internal_type)
children = data.findall(xml_name, ns)
# That's an array
else:
if internal_type: # Complex type, ignore itemsName and use the complex type name
items_name = internal_type_xml_map.get('name', internal_type.__name__)
ns = internal_type_xml_map.get("ns", None)
items_name, ns = _extract_name_from_internal_type(internal_type)
else:
items_name = xml_desc.get("itemsName", xml_name)
children = data.findall(items_name, ns)
Expand Down
50 changes: 50 additions & 0 deletions tests/test_xml_serialization.py
Original file line number Diff line number Diff line change
Expand Up @@ -471,6 +471,56 @@ class XmlModel(Model):

assert result.age == 37

def test_complex_namespace(self):
"""Test recursive namespace."""
basic_xml = """<?xml version="1.0"?>
<entry xmlns="http://www.w3.org/2005/Atom">
<author>
<name>lmazuel</name>
</author>
<AuthorizationRules xmlns="http://schemas.microsoft.com/netservices/2010/10/servicebus/connect">
<AuthorizationRule>
<KeyName>testpolicy</KeyName>
</AuthorizationRule>
</AuthorizationRules>
</entry>"""

class XmlRoot(Model):
_attribute_map = {
'author': {'key': 'author', 'type': 'QueueDescriptionResponseAuthor'},
'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'}},
}
_xml_map = {
'name': 'entry', 'ns': 'http://www.w3.org/2005/Atom'
}

class QueueDescriptionResponseAuthor(Model):
_attribute_map = {
'name': {'key': 'name', 'type': 'str', 'xml': {'ns': 'http://www.w3.org/2005/Atom'}},
}
_xml_map = {
'ns': 'http://www.w3.org/2005/Atom'
}

class AuthorizationRule(Model):
_attribute_map = {
'key_name': {'key': 'KeyName', 'type': 'str', 'xml': {'ns': 'http://schemas.microsoft.com/netservices/2010/10/servicebus/connect'}},
}
_xml_map = {
'ns': 'http://schemas.microsoft.com/netservices/2010/10/servicebus/connect'
}


s = Deserializer({
"XmlRoot": XmlRoot,
"QueueDescriptionResponseAuthor": QueueDescriptionResponseAuthor,
"AuthorizationRule": AuthorizationRule,
})
result = s(XmlRoot, basic_xml, "application/xml")

assert result.author.name == "lmazuel"
assert result.authorization_rules[0].key_name == "testpolicy"


class TestXmlSerialization:

Expand Down