82 lines
2.9 KiB
Python
82 lines
2.9 KiB
Python
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)
|