-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathtxml.py
More file actions
476 lines (401 loc) · 17.5 KB
/
Copy pathtxml.py
File metadata and controls
476 lines (401 loc) · 17.5 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
from xml.etree.ElementTree import iterparse, ParseError
from io import StringIO
from os.path import isfile
from re import findall
class XmlParser:
def __init__(self, source=""):
self.source = source
self.proces_file = False
self.use_io = False
self.encoding = 'UTF-8'
self.namespaces = {}
self.namespace_present = False
self._source_check()
# see also _get_encoding, _get_namespaces
def _source_check(self):
"""
[Function checkes whether the source input is a existing xml file
or a xml syle formatted string]
"""
_extension = self.source[-3:]
if _extension == "xml" or _extension == "xsd":
if isfile(self.source):
self.proces_file = True
self._get_encoding()
self._get_namespaces()
else:
print("File not found {}".format(self.source))
else:
context_test = iterparse(StringIO("""{}""".format(self.source)))
try:
context_test.__next__()
del context_test
self.proces_file = True
self.use_io = True
self._get_encoding()
self._get_namespaces()
except ParseError:
del context_test
print("Input is not in supported Xml format")
def _get_encoding(self):
if self.proces_file and not self.use_io:
with open(self.source, 'r') as f:
l = f.readline()
if 'encoding' in l:
match = findall('(encoding=.*\?)', l)
encoding = match[0].split('=')[1].replace(
'?', '').replace('\"', '')
self.encoding = encoding
# see also get_all_tags
def _get_namespaces(self):
"""[Creates a dictionary of the namespaces with their associated tags ]
Returns:
[dict] -- [Dictionary with namespaces as keys
and the corresponding tags in a list as value ]
"""
tags = self.get_all_tags()
namespaces = {}
for tag in tags:
namespace = findall('({.{1,}})', tag)
if len(namespace) > 0:
namespace = namespace[0]
formatted_tag = tag.replace(namespace, '')
try:
namespaces[namespace].append(formatted_tag)
except KeyError:
namespaces[namespace] = [formatted_tag]
if namespaces:
self.namespace_present = True
self.namespaces = namespaces
# return namespaces
def get_all_tags(self):
"""[All the unique tags available in the Xml
No hierachy is mainted for the xml structure]
Returns:
[list] -- [A list of all the unique tags available in the Xml]
"""
if self.source and self.proces_file:
if self.use_io:
context = iterparse(StringIO("""{}""".format(self.source)),
events=("start",))
else:
data = open(self.source, 'r', encoding=self.encoding)
context = iterparse(data, events=("start",))
else:
print("No source XML-file provided")
return
tag_set = []
for event, elem in context:
tag_set.append(elem.tag)
elem.clear()
if self.source and self.proces_file and not self.use_io:
data.close() # close filestream
del context
tag_set = list(set(tag_set))
return tag_set
# see also search_nodes
def search_namespace_node(self, namespace="", tag=""):
ntag = "{}{}".format(namespace, tag)
for node in self.search_nodes(tag=ntag):
yield node
# see also search_node_attr
def search_namespace_attr(self, namespace="", tag="", **kwargs):
ntag = "{}{}".format(namespace, tag)
for node in self.search_node_attr(tag=ntag, kwargs=kwargs):
yield node
# see also seach_nodes
def search_node_attr(self, tag="", get_children=True, **kwargs):
"""[This function filters results from the <search_node> function
based on given attributes,values]
Keyword Arguments:
tag {str} -- [tag of Xml node element] (default: {""})
get_children {bool} -- [Choice for whether subnodes
should be returned] (default: {True})
Returns / yields:
[dict] -- [Dictionary containing all matching nodes]
"""
if 'kwargs' in kwargs:
kwargs = kwargs['kwargs']
for node in self.search_nodes(tag=tag, get_children=get_children):
if len(kwargs) > 0:
for key in kwargs:
arg = kwargs[key]
try:
node_val = node['element']['attr'][key]
except KeyError:
# print("Key '{}' not found in element {}".format(key,
# tag))
# exit function if non-existing key is requested
node_val = ''
if node_val == arg:
give_node = True
else:
# attribute not matching
# move on to next node
give_node = False
break
else:
give_node = True
if give_node:
yield node
# see also _node_to_dict and _stack_state_controller
def search_nodes(self, tag="", get_children=True):
"""[If a tag is specified the function returns an generator
with all Xml elements which have a matching tag.
If tag is not specified, the root node is returned
When get_children is set, the function returns the subnodes
nested in a list of dictionaries]
Keyword Arguments:
tag {str} -- [tag of Xml node element] (default: {""})
get_children {bool} -- [Choice for whether subnodes
should be returned] (default: {True})
"""
if self.source and self.proces_file:
if self.use_io:
context = iterparse(StringIO("""{}""".format(self.source)),
events=('start', 'end'))
else:
data = open(self.source, 'r', encoding=self.encoding)
context = iterparse(data, events=('start', 'end'))
else:
print("Unable to process input")
return
if get_children:
children = []
p_stack = []
tag_stack = []
p_tag = ""
c_tag = ""
npd = False
append_children = False
for event, elem in context:
if not tag:
# if no tag is given then get data for entire document
tag = elem.tag
if get_children:
if elem.tag != tag and append_children:
event, elem, p_tag, c_tag, p_stack, \
tag_stack, children, npd = \
self._stack_state_controller(event=event,
elem=elem,
p_tag=p_tag,
c_tag=c_tag,
p_stack=p_stack,
tag_stack=tag_stack,
children=children,
npd=npd)
if elem.tag == tag and event == 'start':
append_children = True
if elem.tag == tag and event == 'end':
node_dict = self._node_to_dict(elem)
output_dict = {'element': node_dict, 'children': []}
elem.clear()
if get_children:
output_dict['children'] = children
children = []
append_children = False
yield output_dict
del context
if self.source and self.proces_file and not self.use_io:
data.close() # close filestream
del data
# see also node_to_dict
def _stack_state_controller(self, event, elem, p_tag="", c_tag="",
p_stack=[], tag_stack=[], children=[],
npd=False):
"""[Keeps track of a dictionary stack and a tag stack, and updates them as required.
This is done based on the start/end triggers from the elements in the Xml format]
Arguments:
event {[str]} -- [start/end points of element]
elem {[et.etree.ElementTree.Element]} -- [description]
Keyword Arguments:
p_tag {str} -- [Current parent tag (top of dict stack). (not used actively) ] (default: {""})
c_tag {str} -- [Current child tag (top of tag stack)] (default: {""})
p_stack {list} -- [Stack for holding the parent dictionaries ] (default: {[]})
tag_stack {list} -- [Stack for holding all the tags] (default: {[]})
children {list} -- [List for holding all subnodes found] (default: {[]})
npd {bool} -- [When set new dictionary is appended to stack] (default: {False})
Returns:
All arguments passed to it are returned after being updated
"""
# ndp controls the creation of new dicts in the p_stack
if (elem.tag != c_tag) and (event == "start"):
tag_stack.append(elem.tag)
if npd:
# add new dictionary when children are confiremed to exist
_p_dict = {'children': [], 'element': ""}
p_stack.append(_p_dict)
p_tag = c_tag
c_tag = elem.tag
npd = True
elif (elem.tag == c_tag) and (event == "end"):
if len(tag_stack) == 1:
# last child on stack
if len(p_stack) > 0:
# child has children
_child = p_stack.pop()
_child['element'] = self._node_to_dict(elem)
else:
_child = {'children': [],
'element': self._node_to_dict(elem)}
children.append(_child)
c_tag = ""
tag_stack.pop()
elif len(tag_stack) == len(p_stack):
_child = p_stack.pop()
_parent = p_stack.pop()
_child['element'] = self._node_to_dict(elem)
_parent['children'].append(_child)
p_stack.append(_parent)
tag_stack.pop()
c_tag = tag_stack[-1]
if len(tag_stack) > 1:
p_tag = tag_stack[-2]
else:
p_tag = ""
else:
_parent = p_stack.pop()
_child = self._node_to_dict(elem)
_parent['children'].append(_child)
p_stack.append(_parent)
tag_stack.pop()
c_tag = tag_stack[-1]
if len(tag_stack) > 1:
p_tag = tag_stack[-2]
else:
p_tag = ""
npd = False
elem.clear()
return [event, elem, p_tag, c_tag, p_stack,
tag_stack, children, npd]
def _node_to_dict(self, node=""):
"""[Convert node element attributes to dictionary]
Keyword Arguments:
node {et.etree.ElementTree.Element} -- [] (default: {""})
Returns:
[dict] -- [Dictionary containing all the attribute,value pairs
contained in the node]
"""
data = {}
data['attr'] = {n[0]: n[1] for n in node.items()}
data['text'] = node.text
data['tag'] = node.tag
return data
class XsdtoDict:
def __init__(self, source=''):
self.source = source
def convert_to_dict(self):
parser = XmlParser(source=self.source)
xsd_tags = self.get_export_type_data(parser)
data = {}
for tag in xsd_tags:
data[tag['name']] = self.parse_xml_entry(tag, parser)
return data
def get_export_type_data(self, validation_parser):
all_nodes = validation_parser.search_nodes()
output_types = []
for nodes in all_nodes:
if nodes:
output_types = [{'name': entry['element']['attr']['name'],
'tag': entry['element']['tag']}
for entry in nodes['children']]
return output_types
def parse_xml_entry(self, tag_data, xml_iterator):
parent_tag = tag_data['tag']
parent_name = tag_data['name']
sub_elements = xml_iterator.search_node_attr(tag=parent_tag,
name=parent_name)
if 'complexType' in parent_tag:
output = self.parse_complextypes(sub_elements)
elif 'simpleType' in parent_tag:
output = self.parse_simpletypes(sub_elements)
else:
output = list(sub_elements)
return output
def parse_complextypes(self, complex_iterator):
output = {}
for element_data in complex_iterator:
output['attr'] = element_data['element']['attr']
output['sequence'] = []
if element_data['children']:
for sub_element in element_data['children']:
if 'sequence' in sub_element['element']['tag']:
sequence_data = self.parse_sequence(sub_element['children'])
output['sequence'].append(sequence_data)
else:
pass
return output
def parse_sequence(self, sequence_elements):
sequence_output = []
for element in sequence_elements:
element_data = self.parse_element(element)
sequence_output.append(element_data)
return sequence_output
def parse_element(self, element):
output = {}
if 'children' in element:
output['tag'] = element['element']['tag']
output['attr'] = element['element']['attr']
element_children = element['children']
output['children_data'] = []
for child in element_children:
if 'simpleType' in child['element']['tag']:
child_data = self.parse_simpletypes(child)
output['children_data'].append(child_data)
else:
output['tag'] = element['tag']
output['attr'] = element['attr']
return output
def parse_simpletypes(self, simple_element):
output = {}
try:
element_children = simple_element['children']
for child_element in element_children:
if 'restriction' in child_element['element']['tag']:
output['restrictions'] = {'attr': child_element['element']['attr']}
restriction_data = self.parse_restrictions(child_element['children'])
output['restrictions']['restrictions'] = restriction_data
except TypeError:
element_data = list(simple_element)
element_data = element_data[0]
element_children = element_data['children']
element_children = element_children[0]['children']
output['restrictions'] = []
for data in element_children:
if 'element' in data:
output['restrictions'].append(data['element']['attr'])
else:
if 'minLength' in data['tag']:
output['restrictions'].append({'minlength':data['attr']})
if 'maxLength' in data['tag']:
output['restrictions'].append({'maxlength':data['attr']})
return output
def parse_restrictions(self, restriction_iterator):
output = []
for restriction in restriction_iterator:
restriction_data = {}
restriction_data['enumarations'] = []
restriction_data['length_data'] = []
if 'element' in restriction:
if 'enumeration' in restriction['element']['tag']:
enumaration_data = self.parse_enumarations(restriction['children'])
restriction_data['enumarations'].append(enumaration_data)
restriction_data['attr'] = restriction['element']['attr']
elif 'Length' in restriction['element']['tag']:
restriction_data['attr'] = restriction['element']['attr']
restriction_data['length_data'].append(restriction['element']['attr'])
else:
restriction_data['attr'] = restriction['attr']
output.append(restriction_data)
return output
def parse_enumarations(self, enumeration_iterator):
output = {'annotations': ""}
for enumaration in enumeration_iterator:
if 'annotation' in enumaration['element']['tag']:
annotations = enumaration['children']
annot = {'documentation': []}
for annotation in annotations:
annot['documentation'].append({'attr': annotation['attr'],
'text': ['text']})
output['annotations'] = annot
return output