add an XmlBuilder class to help in generating XML documents (#9163)
This commit is contained in:
parent
d8bc2a9fcd
commit
2fceba9aa4
|
@ -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)
|
Loading…
Reference in New Issue