Skip to content

Instantly share code, notes, and snippets.

@dolph
Created February 10, 2012 21:10
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save dolph/1792904 to your computer and use it in GitHub Desktop.
Save dolph/1792904 to your computer and use it in GitHub Desktop.
Dict to XML serialization
"""
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
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