2828
2929- Process XML that is not DataONE types, and so does not have PyXB bindings.
3030
31- - Process XML that is invalid in such a day that PyXB cannot parse or generate
31+ - Process XML that is invalid in such a way that PyXB cannot parse or generate
3232it.
3333
34- - Process XML without causing xs:dateTime fields from being normalized to the
35- UTC time zone (PyXB is based on the XML DOM, which requires such normalization).
34+ - Process XML without causing xs:dateTime fields to be normalized to the UTC
35+ time zone (PyXB is based on the XML DOM, which requires such normalization.)
3636
3737- Generate intentionally invalid XML for DataONE types in order to test how MNs,
3838CNs and other components of the DataONE architecture handle and recover from
8080import d1_common .types .exceptions
8181import d1_common .xml
8282
83+ import xml .etree .ElementTree
8384
8485@contextlib2 .contextmanager
8586def wrap (xml_str ):
@@ -114,7 +115,7 @@ def get_pretty_xml(self, encoding='unicode'):
114115
115116 def get_xml_below_element (self , el_name , el_idx = 0 , encoding = 'unicode' ):
116117 """Return an XML fragment rooted at {el}"""
117- return ET .tostring (self .get_element (el_name , el_idx ), encoding )
118+ return ET .tostring (self .get_element_by_name (el_name , el_idx ), encoding )
118119
119120 def get_element_list_by_name (self , el_name ):
120121 """Return a list of elements with name {el_name}
@@ -128,8 +129,21 @@ def get_element_list_by_attr_key(self, attr_key):
128129
129130 # get element
130131
131- def get_element (self , el_name , el_idx = 0 ):
132- """Return the selected element"""
132+ def get_element_by_xpath (self , xpath_str , namespaces = None ):
133+ """Return a list of elements by xpath
134+ - A list is always returned, even when there are zero or one matches.
135+ """
136+ try :
137+ return self ._root_el .findall ('.' + xpath_str , namespaces )
138+ except (ValueError , xml .etree .ElementTree .ParseError ) as e :
139+ raise SimpleXMLWrapperException (
140+ 'XPath select raised exception. xpath_str="{}" error="{}"' .format (
141+ xpath_str , str (e )
142+ )
143+ )
144+
145+ def get_element_by_name (self , el_name , el_idx = 0 ):
146+ """Return the selected element by element name"""
133147 el_list = self .get_element_list_by_name (el_name )
134148 try :
135149 return el_list [el_idx ]
@@ -154,11 +168,11 @@ def get_element_by_attr_key(self, attr_key, el_idx=0):
154168
155169 def get_element_text (self , el_name , el_idx = 0 ):
156170 """Return the text of the selected element"""
157- return self .get_element (el_name , el_idx ).text
171+ return self .get_element_by_name (el_name , el_idx ).text
158172
159173 def set_element_text (self , el_name , el_text , el_idx = 0 ):
160174 """Set the text of the selected element"""
161- self .get_element (el_name , el_idx ).text = el_text
175+ self .get_element_by_name (el_name , el_idx ).text = el_text
162176
163177 # set/get text by attr key
164178
@@ -192,7 +206,7 @@ def get_element_dt(self, el_name, tz=None, el_idx=0):
192206 timezone, other contextual information is required in order to determine
193207 the exact represented time.
194208 - {tz}=d1_common.date_time.UTC(): Set naive dt to UTC."""
195- return iso8601 .parse_date (self .get_element (el_name , el_idx ).text , tz )
209+ return iso8601 .parse_date (self .get_element_by_name (el_name , el_idx ).text , tz )
196210
197211 def set_element_dt (self , el_name , dt , tz = None , el_idx = 0 ):
198212 """Set the text of the selected element to an ISO8601 formatted datetime
@@ -205,13 +219,13 @@ def set_element_dt(self, el_name, dt, tz=None, el_idx=0):
205219 the exact represented time.
206220 - {tz}=d1_common.date_time.UTC(): Set naive {dt} to UTC."""
207221 dt = d1_common .date_time .cast_naive_datetime_to_tz (dt , tz )
208- self .get_element (el_name , el_idx ).text = dt .isoformat ()
222+ self .get_element_by_name (el_name , el_idx ).text = dt .isoformat ()
209223
210224 # remove
211225
212226 def remove_children (self , el_name , el_idx = 0 ):
213227 """Remove any child elements from the selected element"""
214- self .get_element (el_name , el_idx )[:] = []
228+ self .get_element_by_name (el_name , el_idx )[:] = []
215229
216230 # replace subtree
217231
@@ -220,7 +234,7 @@ def replace_by_etree(self, root_el, el_idx=0):
220234 the selected element with {root_el}
221235 - {root_el} can be a single element or the root of an element tree.
222236 """
223- el = self .get_element (root_el .tag , el_idx )
237+ el = self .get_element_by_name (root_el .tag , el_idx )
224238 el [:] = list (root_el )
225239 el .attrib = root_el .attrib
226240
0 commit comments