|
1 | 1 | try:
|
2 | 2 | import lxml.etree as ET
|
| 3 | + # If we're using lxml we have to do some extra work to support namespaces, |
| 4 | + # so we have a variable to check whether we're using lxml: |
| 5 | + USING_LXML = True |
3 | 6 | except ImportError:
|
4 | 7 | import xml.etree.ElementTree as ET
|
| 8 | + USING_LXML = False |
5 | 9 | from warnings import warn
|
6 | 10 | from flattentool.exceptions import DataErrorWarning
|
7 | 11 |
|
8 | 12 |
|
9 |
| -def child_to_xml(parent_el, tagname, child): |
| 13 | +def child_to_xml(parent_el, tagname, child, toplevel=False, nsmap=None): |
10 | 14 | if hasattr(child, 'items'):
|
11 |
| - child_el = dict_to_xml(child, tagname) |
| 15 | + child_el = dict_to_xml(child, tagname, toplevel=False, nsmap=nsmap) |
12 | 16 | if child_el is not None:
|
13 | 17 | parent_el.append(child_el)
|
14 | 18 | else:
|
15 | 19 | if tagname.startswith('@'):
|
| 20 | + if USING_LXML and toplevel and tagname.startswith('@xmlns'): |
| 21 | + nsmap[tagname[1:].split(':', 1)[1]] = str(child) |
| 22 | + return |
16 | 23 | try:
|
17 |
| - parent_el.attrib[tagname[1:]] = str(child) |
| 24 | + attr_name = tagname[1:] |
| 25 | + if USING_LXML and ':' in attr_name: |
| 26 | + attr_name = '{' + nsmap.get(attr_name.split(':', 1)[0], '') + '}' + attr_name.split(':', 1)[1] |
| 27 | + parent_el.attrib[attr_name] = str(child) |
18 | 28 | except ValueError as e:
|
19 | 29 | warn(str(e), DataErrorWarning)
|
20 | 30 | elif tagname == 'text()':
|
21 | 31 | parent_el.text = str(child)
|
22 | 32 | else:
|
23 |
| - raise('Everything should end with text() or an attirbute!') |
| 33 | + raise('Everything should end with text() or an attribute!') |
24 | 34 |
|
25 | 35 |
|
26 |
| -def dict_to_xml(data, tagname): |
| 36 | +def dict_to_xml(data, tagname, toplevel=True, nsmap=None): |
| 37 | + if USING_LXML and ':' in tagname and not toplevel: |
| 38 | + tagname = '{' + nsmap.get(tagname.split(':', 1)[0], '') + '}' + tagname.split(':', 1)[1] |
27 | 39 | try:
|
28 |
| - el = ET.Element(tagname) |
| 40 | + if USING_LXML: |
| 41 | + el = ET.Element(tagname, nsmap=nsmap) |
| 42 | + else: |
| 43 | + el = ET.Element(tagname) |
29 | 44 | except ValueError as e:
|
30 | 45 | warn(str(e), DataErrorWarning)
|
31 | 46 | return
|
32 | 47 |
|
33 | 48 | for k, v in data.items():
|
34 | 49 | if type(v) == list:
|
35 | 50 | for item in v:
|
36 |
| - child_to_xml(el, k, item) |
| 51 | + child_to_xml(el, k, item, nsmap=nsmap) |
37 | 52 | else:
|
38 |
| - child_to_xml(el, k, v) |
| 53 | + child_to_xml(el, k, v, toplevel=toplevel, nsmap=nsmap) |
39 | 54 | return el
|
40 | 55 |
|
41 | 56 |
|
42 | 57 | def toxml(data):
|
43 |
| - return ET.tostring(dict_to_xml(data, 'iati-activities')) |
| 58 | + nsmap = { |
| 59 | + # This is "bound by definition" - see https://www.w3.org/XML/1998/namespace |
| 60 | + 'xml': 'http://www.w3.org/XML/1998/namespace' |
| 61 | + } |
| 62 | + return ET.tostring(dict_to_xml(data, 'iati-activities', nsmap=nsmap)) |
0 commit comments