Created
February 10, 2012 21:10
-
-
Save dolph/1792904 to your computer and use it in GitHub Desktop.
Dict to XML serialization
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
""" | |
Dict to XML serializer. | |
The identity API prefers attributes over elements, so we serialize that way | |
by convention, and TODO(dolph): configure exceptions. | |
""" | |
from lxml import etree | |
# illustrating an easy * complex test cases, respectively | |
# Source JSON: https://github.com/openstack/keystone/blob/master/keystone/content/common/samples/auth_credentials.json | |
# Corresponding XML: https://github.com/openstack/keystone/blob/master/keystone/content/common/samples/auth_credentials.xml | |
AUTH = {'auth': {'tenantName': 'project-x', 'passwordCredentials': {'username': 'admin', 'password': 'secret'}}} | |
# Source JSON: https://github.com/openstack/keystone/blob/master/keystone/content/common/samples/auth.json | |
# Corresponding XML: https://github.com/openstack/keystone/blob/master/keystone/content/common/samples/auth.xml | |
AUTH_RESPONSE = { | |
'access': { | |
'token': { | |
'id': 'ab48a9efdfedb23ty3494', | |
'expires': '2010-11-01T03:32:15-05:00', | |
'tenant': { | |
'id': 't1000', | |
'name': 'My Project' | |
}, | |
'tenants': [{ | |
'id': 't1000', | |
'name': 'My Project' | |
}] | |
}, | |
'user': { | |
'id': 'u123', | |
'name': 'jqsmith', | |
'roles': [ | |
{ | |
'id': '100', | |
'name': 'compute:admin' | |
}, | |
{ | |
'id': '101', | |
'name': 'object-store:admin', | |
'tenantId': 't1000' | |
} | |
], | |
'roles_links': [] | |
}, | |
'serviceCatalog': [ | |
{ | |
'name': 'Cloud Servers', | |
'type': 'compute', | |
'endpoints': [ | |
{ | |
'id': '1', | |
'tenantId': 't1000', | |
'publicURL': 'https://compute.north.host.com/v1/t1000', | |
'internalURL': 'https://compute.north.internal/v1/t1000', | |
'region': 'North', | |
'versionId': '1', | |
'versionInfo': 'https://compute.north.host.com/v1/', | |
'versionList': 'https://compute.north.host.com/' | |
}, | |
{ | |
'id': '2', | |
'tenantId': 't1000', | |
'publicURL': 'https://compute.north.host.com/v1.1/t1000', | |
'internalURL': 'https://compute.north.internal/v1.1/t1000', | |
'region': 'North', | |
'versionId': '1.1', | |
'versionInfo': 'https://compute.north.host.com/v1.1/', | |
'versionList': 'https://compute.north.host.com/' | |
} | |
], | |
'endpoints_links': [] | |
}, | |
{ | |
'name': 'Cloud Files', | |
'type': 'object-store', | |
'endpoints': [ | |
{ | |
'tenantId': 't1000', | |
'publicURL': 'https://storage.north.host.com/v1/t1000', | |
'internalURL': 'https://storage.north.internal/v1/t1000', | |
'region': 'North', | |
'versionId': '1', | |
'versionInfo': 'https://storage.north.host.com/v1/', | |
'versionList': 'https://storage.north.host.com/' | |
}, | |
{ | |
'tenantId': 't1000', | |
'publicURL': 'https://storage.south.host.com/v1/t1000', | |
'internalURL': 'https://storage.south.internal/v1/t1000', | |
'region': 'South', | |
'versionId': '1', | |
'versionInfo': 'https://storage.south.host.com/v1/', | |
'versionList': 'https://storage.south.host.com/' | |
} | |
] | |
}, | |
{ | |
'name': 'DNS-as-a-Service', | |
'type': 'dnsextension:dns', | |
'endpoints': [ | |
{ | |
'tenantId': 't1000', | |
'publicURL': 'https://dns.host.com/v2.0/t1000', | |
'versionId': '2.0', | |
'versionInfo': 'https://dns.host.com/v2.0/', | |
'versionList': 'https://dns.host.com/' | |
} | |
] | |
} | |
] | |
} | |
} | |
DOCTYPE = '<?xml version="1.0" encoding="UTF-8"?>' | |
XMLNS = 'http://docs.openstack.org/identity/api/v2.0' | |
def serialize(d): | |
"""Serialize a dictionary to XML""" | |
assert len(d.keys()) == 1, 'Cannot encode more than one root element' | |
# name the root dom element | |
name = d.keys()[0] | |
# only the root dom element gets an xlmns, TODO(dolph): ... I think? | |
root = etree.Element(name, xmlns=XMLNS) | |
populate_element(root, d[name]) | |
# TODO(dolph): we don't actually need to pretty print for real clients | |
# TODO(dolph): i think there's a way to get a doctype from lxml? | |
return '%s\n%s' % (DOCTYPE, etree.tostring(root, pretty_print=True)) | |
def populate_element(element, d): | |
"""Populates an etree with the given dictionary""" | |
for k, v in d.iteritems(): | |
if type(v) is dict: | |
# serialize the child dictionary | |
child = etree.Element(k) | |
populate_element(child, v) | |
element.append(child) | |
elif type(v) is list: | |
# serialize the child list | |
# TODO(dolph): this is where the spec has a LOT of inconsistency, but this covers 80% of it | |
if k[-1] == 's': | |
name = k[:-1] | |
else: | |
name = k | |
for item in v: | |
child = etree.Element(name) | |
populate_element(child, item) | |
element.append(child) | |
else: | |
# add attributes to the current element | |
element.set(k, unicode(v)) | |
if __name__ == '__main__': | |
print serialize(AUTH) | |
print serialize(AUTH_RESPONSE) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment