@@ -67,19 +67,35 @@ def load_xml(filename: str):
67
67
Args:
68
68
filename: The name of the XML file to load.
69
69
"""
70
+ import re
71
+
70
72
with open (filename , "rb" ) as infile :
71
73
tree = ET .parse (infile ) # Parse the XML file into an ElementTree object
72
74
root = tree .getroot () # Get the root element
73
75
74
- # Convert the ElementTree object to a dictionary
75
- data = element_to_dict (root )
76
- removed_id = handle_id (data )
77
- converted_to_actual_val = convert_values (removed_id )
76
+ # This defines regular expressions to match the namespace patterns to be removed
77
+ ns_prefix_pattern = r"(ns\d+:|:ns\d+)"
78
+
79
+ # Converts the loaded xml into a string and removes unwanted string values ':ns0' to :ns∞ and 'ns0:' to ns∞:
80
+ # They prevent the xml from loading correctly
81
+ xml_string = ET .tostring (root ).decode ()
82
+ cleaned_xml = re .sub (ns_prefix_pattern , "" , xml_string ).strip ()
83
+
84
+ # Removes xmlns, xmlns:xsi and xsi:schemaLocation from the xml structure for conversion
85
+ # it passes an element tree object to the elementtree_element_to_dict function
86
+ removed_namespaces = process_xml_namespace (cleaned_xml )
87
+
88
+ # Converts the resulting xml stripped of xmlns, xmlns:xsi and xsi:schemaLocation into a dict
89
+ data = elementtree_element_to_dict (removed_namespaces )
78
90
79
- return convert_values (converted_to_actual_val )
91
+ # Removes every key having 'id' and replaces it with it's value
92
+ removed_id = handle_xml_dict_id (data )
80
93
94
+ # Values are returned as strings after conversion, this corrects them to their actual values
95
+ return convert_xml_dict_values (removed_id )
81
96
82
- def element_to_dict (element ):
97
+
98
+ def elementtree_element_to_dict (element ):
83
99
"""
84
100
This convert an ElementTree element to a dictionary.
85
101
@@ -94,35 +110,57 @@ def element_to_dict(element):
94
110
if attrs :
95
111
result .update (attrs )
96
112
113
+ children_by_tag = {}
97
114
for child_element in element :
98
- child_key = child_element .tag
99
- child_value = element_to_dict (child_element )
115
+ child_key = child_element .tag + "s"
116
+ child_value = elementtree_element_to_dict (child_element )
100
117
101
- if child_key in result :
102
- if not isinstance (result [child_key ], list ):
103
- result [child_key ] = [result [child_key ]]
104
- result [child_key ].append (child_value )
105
- else :
118
+ # Check if the child element has an 'id' attribute
119
+ if "id" in child_element .attrib :
120
+ # If the child element has an 'id', add it to the result dictionary directly
106
121
result [child_key ] = child_value
122
+ else :
123
+ # If the child element does not have an 'id', represent it as a list
124
+ children_by_tag .setdefault (child_key , []).append (child_value )
125
+
126
+ # Append the lists to the result dictionary
127
+ result .update (children_by_tag )
107
128
108
129
return result
109
130
110
131
111
- def handle_id (dictionary ):
132
+ def process_xml_namespace (xml_string ):
133
+ # Remove ignored elements from the XML string
134
+ ignored_elements = [
135
+ 'xmlns="http://www.neuroml.org/schema/neuroml2"' ,
136
+ 'xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"' ,
137
+ 'xsi:schemaLocation="http://www.neuroml.org/schema/neuroml2 https://raw.github.com/NeuroML/NeuroML2/development/Schemas/NeuroML2/NeuroML_v2.3.xsd"' ,
138
+ ]
139
+
140
+ # Loops through the xml string and removes every instance of the elements in the list named ignored_elements
141
+ for ignored_element in ignored_elements :
142
+ xml_string = xml_string .replace (ignored_element , "" ).strip ()
143
+
144
+ # Parse the XML string into an ElementTree
145
+ root = ET .fromstring (xml_string )
146
+ return root
147
+
148
+
149
+ def handle_xml_dict_id (dictionary ):
112
150
if isinstance (dictionary , dict ):
113
151
if "id" in dictionary :
114
152
nested_dict = {dictionary ["id" ]: dictionary .copy ()}
115
153
del nested_dict [dictionary ["id" ]]["id" ]
116
- return {k : handle_id (v ) for k , v in nested_dict .items ()}
154
+ return {k : handle_xml_dict_id (v ) for k , v in nested_dict .items ()}
117
155
else :
118
- return {k : handle_id (v ) for k , v in dictionary .items ()}
156
+ return {k : handle_xml_dict_id (v ) for k , v in dictionary .items ()}
119
157
elif isinstance (dictionary , list ):
120
- return [handle_id (item ) for item in dictionary ]
158
+ return [handle_xml_dict_id (item ) for item in dictionary ]
121
159
else :
122
160
return dictionary
123
161
124
162
125
- def convert_values (value ):
163
+ def convert_xml_dict_values (value ):
126
164
"""
127
165
This recursively converts values to their actual types.
128
166
@@ -146,9 +184,9 @@ def convert_values(value):
146
184
elif value .lower () == "none" :
147
185
return None
148
186
elif isinstance (value , dict ):
149
- return {key : convert_values (val ) for key , val in value .items ()}
187
+ return {key : convert_xml_dict_values (val ) for key , val in value .items ()}
150
188
elif isinstance (value , list ):
151
- return [convert_values (item ) for item in value ]
189
+ return [convert_xml_dict_values (item ) for item in value ]
152
190
153
191
return value
154
192
@@ -219,11 +257,20 @@ def build_xml_element(data, parent=None):
219
257
for child in children :
220
258
child_element = build_xml_element (child )
221
259
parent .append (child_element )
222
- else :
260
+
261
+ # Filters name space and schemaLoacation attributes, only allows non name space attributes to be added as attributes
262
+ elif not isinstance (aattr .default , str ):
223
263
attribute_name = aattr .name
224
264
attribute_value = data .__getattribute__ (aattr .name )
225
265
parent .set (attribute_name , str (attribute_value ))
226
266
267
+ # This defines the various namespaces and schemaLocation of the generated xml
268
+ if hasattr (data , "xmlns" ):
269
+ parent .set ("xmlns" , data .xmlns )
270
+ if hasattr (data , "xmlns_xsi" ):
271
+ parent .set ("xmlns:xsi" , data .xmlns_xsi )
272
+ if hasattr (data , "xmlns_loc" ):
273
+ parent .set ("xsi:schemaLocation" , str (data .xmlns_loc ))
227
274
return parent
228
275
229
276
0 commit comments