From c0819dbb152ea21e3c4c0e6952d29ae5e930efe4 Mon Sep 17 00:00:00 2001 From: Benjamin Dauvergne Date: Wed, 2 Dec 2015 16:12:09 +0100 Subject: [PATCH 2/5] add an XmlBuilder class to help in generating XML documents (#9163) --- passerelle/xml_builder.py | 81 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 81 insertions(+) create mode 100644 passerelle/xml_builder.py diff --git a/passerelle/xml_builder.py b/passerelle/xml_builder.py new file mode 100644 index 0000000..935e5b2 --- /dev/null +++ b/passerelle/xml_builder.py @@ -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) -- 2.1.4