add an XmlBuilder class to help in generating XML documents (#9163)

This commit is contained in:
Benjamin Dauvergne 2015-12-02 16:12:09 +01:00
parent d8bc2a9fcd
commit 2fceba9aa4
1 changed files with 81 additions and 0 deletions

81
passerelle/xml_builder.py Normal file
View File

@ -0,0 +1,81 @@
import xml.etree.ElementTree as ET
class XmlBuilder(object):
'''Build XML document interpolating value from context using str.format() in
text data. Grammar for schema is:
schema = tree
tree = simple | loop
simple = (tag_name, attribs_dict?, tree*)
loop = ('?loop', loop_var_name, tree*)
where tag_name is an ElementTree tag name, or an XML tag name with prefix
in namespaces, attribs_dict is a list of key values pairs
The loop construct allow to iterate one variable, the one named by loop_var_name, of the
context as a subcontext to pass to sub-schemas inside the loop tuple.
'''
schema = None
encoding = 'us-ascii'
namespaces = None
def __init__(self, schema=None, encoding=None, namespaces=None):
self.namespaces = {}
if schema is not None:
self.schema = schema
if encoding is not None:
self.encoding = encoding
if namespaces is not None:
self.namespaces.update(namespaces)
def resolve_namespace(self, qname):
if ':' in qname:
prefix, name = qname.split(':')
url = self.namespaces[prefix]
return '{{{0}}}{1}'.format(url, name)
return qname
def build(self, context=None):
tb = ET.TreeBuilder()
def helper(schema, context):
tag = schema[0]
if tag == '?loop':
var = schema[1]
for subcontext in context[var]:
for subschema in schema[2:]:
helper(subschema, subcontext)
return
if context:
tag = tag.format(**context)
tag = self.resolve_namespace(tag)
template_attribs = schema[1] if (len(schema) > 0
and isinstance(schema[1], dict)) else None
attribs = None
if template_attribs:
for key, value in template_attribs.items():
if context:
key = key.format(**context)
key = self.resolve_namespace(key)
value = value.format(**context)
attribs[key] = value
tb.start(tag, attribs or {})
if template_attribs is None:
children = schema[1:]
else:
children = schema[2:]
for child in children:
if isinstance(child, basestring):
if context:
child = child.format(**context)
tb.data(child)
else:
helper(child, context)
tb.end(tag)
helper(self.schema, context)
return tb.close()
def string(self, context=None, encoding=encoding):
return ET.tostring(
self.build(context=context),
encoding=encoding or self.encoding)