blob: 8d1dcf4ca9dda09a079c5a91465ff457be8122a7 [file] [log] [blame]
adminbae64d82013-08-01 10:50:15 -07001'''
2Created on 03-Dec-2012
3
4@author: Anil Kumar (anilkumar.s@paxterrasolutions.com)
5
6 TestON is free software: you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation, either version 2 of the License, or
9 (at your option) any later version.
10
11 TestON is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
Jon Hall4ba53f02015-07-29 13:07:41 -070017 along with TestON. If not, see <http://www.gnu.org/licenses/>.
adminbae64d82013-08-01 10:50:15 -070018
19'''
20
21"""
22 xmldict
23 ~~~~~~~~~~~~~~~~~~~~~~~~~
24
25 Convert xml to python dictionaries.
26"""
27import datetime
28
29def xml_to_dict(root_or_str, strict=True):
30 """
31 Converts `root_or_str` which can be parsed xml or a xml string to dict.
32
33 """
34 root = root_or_str
35 if isinstance(root, str):
36 import xml.etree.cElementTree as ElementTree
37 root = ElementTree.XML(root_or_str)
38 try :
39 return {root.tag: _from_xml(root, strict)}
Jon Hall1de6c192015-09-02 13:50:16 -070040 except StandardError:
adminbae64d82013-08-01 10:50:15 -070041 return None
42
43def dict_to_xml(dict_xml):
44 """
45 Converts `dict_xml` which is a python dict to corresponding xml.
46 """
47 return _to_xml(dict_xml)
48
49def _to_xml(el):
50 """
51 Converts `el` to its xml representation.
52 """
53 val = None
54 if isinstance(el, dict):
55 val = _dict_to_xml(el)
56 elif isinstance(el, bool):
57 val = str(el).lower()
58 else:
59 val = el
60 if val is None: val = 'null'
61 return val
62
63def _extract_attrs(els):
64 """
65 Extracts attributes from dictionary `els`. Attributes are keys which start
66 with '@'
67 """
68 if not isinstance(els, dict):
69 return ''
70 return ''.join(' %s="%s"' % (key[1:], value) for key, value in els.iteritems()
71 if key.startswith('@'))
72
73def _dict_to_xml(els):
74 """
75 Converts `els` which is a python dict to corresponding xml.
76 """
77 def process_content(tag, content):
78 attrs = _extract_attrs(content)
79 text = isinstance(content, dict) and content.get('#text', '') or ''
80 return '<%s%s>%s%s</%s>' % (tag, attrs, _to_xml(content), text, tag)
81
82 tags = []
83 for tag, content in els.iteritems():
84 # Text and attributes
85 if tag.startswith('@') or tag == '#text':
86 continue
87 elif isinstance(content, list):
88 for el in content:
89 tags.append(process_content(tag, el))
90 elif isinstance(content, dict):
91 tags.append(process_content(tag, content))
92 else:
93 tags.append('<%s>%s</%s>' % (tag, _to_xml(content), tag))
94 return ''.join(tags)
95
96def _is_xml_el_dict(el):
97 """
98 Returns true if `el` is supposed to be a dict.
99 This function makes sense only in the context of making dicts out of xml.
100 """
101 if len(el) == 1 or el[0].tag != el[1].tag:
102 return True
103 return False
104
105def _is_xml_el_list(el):
106 """
107 Returns true if `el` is supposed to be a list.
108 This function makes sense only in the context of making lists out of xml.
109 """
110 if len(el) > 1 and el[0].tag == el[1].tag:
111 return True
112 return False
113
114def _str_to_datetime(date_str):
115 try:
116 val = datetime.datetime.strptime(date_str, "%Y-%m-%dT%H:%M:%SZ")
117 except ValueError:
118 val = date_str
119 return val
120
121def _str_to_boolean(bool_str):
122 if bool_str.lower() != 'false' and bool(bool_str):
123 return True
124 return False
125
126def _from_xml(el, strict):
127 """
128 Extracts value of xml element element `el`.
129 """
130 val = None
131 # Parent node.
132 if el:
133 if _is_xml_el_dict(el):
134 val = _dict_from_xml(el, strict)
135 elif _is_xml_el_list(el):
136 val = _list_from_xml(el, strict)
137 # Simple node.
138 else:
139 attribs = el.items()
140 # An element with attributes.
141 if attribs and strict:
142 val = dict(('@%s' % k, v) for k, v in dict(attribs).iteritems())
143 if el.text:
144 converted = _val_and_maybe_convert(el)
145 val['#text'] = el.text
146 if converted != el.text:
147 val['#value'] = converted
148 elif el.text:
149 # An element with no subelements but text.
150 val = _val_and_maybe_convert(el)
151 elif attribs:
152 val = dict(attribs)
153 return val
154
155def _val_and_maybe_convert(el):
156 """
157 Converts `el.text` if `el` has attribute `type` with valid value.
158 """
159 text = el.text.strip()
160 data_type = el.get('type')
161 convertor = _val_and_maybe_convert.convertors.get(data_type)
162 if convertor:
163 return convertor(text)
164 else:
165 return text
166_val_and_maybe_convert.convertors = {
167 'boolean': _str_to_boolean,
168 'datetime': _str_to_datetime,
169 'integer': int
170}
171
172def _list_from_xml(els, strict):
173 """
174 Converts xml elements list `el_list` to a python list.
175 """
176
177 temp = {}
178 for el in els:
179 tag = el.attrib["name"]
180 temp[tag] = (_from_xml(el, strict))
181 return temp
182
183def _dict_from_xml(els, strict):
184 """
185 Converts xml doc with root `root` to a python dict.
186 """
187 # An element with subelements.
188 res = {}
189 for el in els:
190 res[el.tag] = _from_xml(el, strict)
191 return res