def test_attribute_name(self): attribute_xpath = XPath('./element/@attri') absolute_attribute_xpath = XPath('/ns:root/nss:element/@nss:attribute') element_xpath = XPath('/root/element/subelement') with self.subTest(): self.assertEqual(attribute_xpath.attribute_name(), 'attri') with self.subTest(): self.assertEqual(absolute_attribute_xpath.attribute_name(), 'nss:attribute') with self.subTest(): with self.assertRaises(ValueError): element_xpath.attribute_name()
def test_node_xpaths(self): root = etree.fromstring(sample_no_namespace) xml_tree = etree.ElementTree(root) xpath_set = {XPath('/catalog/book[1]'), XPath('/catalog/book[1]/@id'), XPath('/catalog/book[1]/author'), XPath('/catalog/book[1]/title'), XPath('/catalog/book[1]/genre'), XPath('/catalog/book[1]/price'), XPath('/catalog/book[1]/publish_date'), XPath('/catalog/book[1]/description'), XPath('/catalog/book[2]'), XPath('/catalog/book[2]/@id'), XPath('/catalog/book[2]/author'), XPath('/catalog/book[2]/title'), XPath('/catalog/book[2]/genre'), XPath('/catalog/book[2]/price'), XPath('/catalog/book[2]/publish_date'), XPath('/catalog/book[2]/description')} with self.subTest(): self.assertCountEqual(xpath_set, generate_node_xpaths(xml_tree)) with self.subTest(): self.assertSetEqual(xpath_set, set(generate_node_xpaths(xml_tree)))
def test_is_attribute(self): attribute_node = XPath('/root/element/@attribute') ns_attribute_node = XPath('/ns:root/nss:element/@nss:attribute') element_node = XPath('root/element') with self.subTest(): self.assertTrue(attribute_node.is_attribute()) with self.subTest(): self.assertTrue(ns_attribute_node.is_attribute()) with self.subTest(): self.assertFalse(element_node.is_attribute())
def test_parent(self): absolute_attribute_xpath = XPath('/ns:root/nss:element/@nss:attribute') relative_element_xpath = XPath('./element/subelement') with self.subTest(): self.assertEqual(absolute_attribute_xpath.parent(), XPath('/ns:root/nss:element')) with self.subTest(): self.assertEqual(relative_element_xpath.parent(), XPath('./element'))
def test_change_attribute_tag(self): with self.subTest(): xpath = XPath('/adrmsg:ADRMessage/adrmsg:hasMember/aixm:Route/@gml:id') reference = JSONPath('$.ADRMessage.hasMember.Route._id') self.assertEqual(reference, xpath.to_json_path(attributes='_', with_namespaces=False)) with self.subTest(): xpath = XPath('/adrmsg:ADRMessage/adrmsg:hasMember/aixm:Route/@gml:id') reference = JSONPath('$.ADRMessage.hasMember.Route.id') self.assertEqual(reference, xpath.to_json_path(attributes='', with_namespaces=False)) with self.subTest(): xpath = XPath('/adrmsg:ADRMessage/adrmsg:hasMember/aixm:Route/@gml:id') reference = JSONPath('$.adrmsg:ADRMessage.adrmsg:hasMember.aixm:Route.attrib_gml:id') self.assertEqual(reference, xpath.to_json_path(attributes='attrib_', with_namespaces=True))
def test_is_relative(self): absolute_xpath = XPath('/root/element/subelement') relative_xpath = XPath('./element/@attribute') with self.subTest(): self.assertFalse(absolute_xpath.is_relative()) with self.subTest(): self.assertTrue(relative_xpath.is_relative())
def test_sequence_path(self): with self.subTest(): xpath = XPath("/adrmsg:ADRMessage/adrmsg:hasMember[1]/aixm:Route/@gml:id") reference = JSONPath("$.adrmsg:ADRMessage.adrmsg:hasMember[0].aixm:Route._gml:id") self.assertEqual(reference, xpath.to_json_path(attributes="_", with_namespaces=True)) with self.subTest(): xpath = XPath("/adrmsg:ADRMessage/adrmsg:hasMember[1]/aixm:Route/@gml:id") reference = JSONPath("$.ADRMessage.hasMember[0].Route.id") self.assertEqual(reference, xpath.to_json_path(attributes="", with_namespaces=False)) with self.subTest(): xpath = XPath("/adrmsg:ADRMessage/adrmsg:hasMember[-1]/aixm:Route/@gml:id") reference = JSONPath("$.ADRMessage.hasMember[-1].Route.id") self.assertRaises(ValueError)
def test_index_removal(self): xpath = XPath('/root/element[10]/subelement/subsubelement[1]/@attribute') reference = XPath('/root/element/subelement/subsubelement/@attribute') with self.subTest(): self.assertEqual(xpath.remove_indices(in_place=False), reference) with self.subTest(): xpath.remove_indices(in_place=True) self.assertEqual(xpath, reference)
def test_namespace_substitution(self): xpath = XPath('/adrmsg:ADRMessage/adrmsg:hasMember[1]/aixm:Route/@{http://www.opengis.net/gml/3.2}id') short_xpath = XPath('/adrmsg:ADRMessage/adrmsg:hasMember[1]/aixm:Route/@gml:id') with self.subTest(): self.assertEqual(short_xpath, xpath.shorten_namespaces(self.namespaces, in_place=False)) with self.subTest(): xpath.shorten_namespaces(self.namespaces, in_place=True) self.assertEqual(short_xpath, xpath)
def infer_path_type(path: str) -> Union[XPath, JSONPath]: """ Infers the type of a path (XPath or JSONPath) based on its syntax. It performs some basic sanity checks to differentiate a JSONPath from an XPath. :param path: A valid XPath or JSONPath string. :return: An instance of JSONPath or XPath """ if not path: raise ValueError("No path given") if path[0] in ['$', '@']: return JSONPath(path) else: if path[0] in ['.', '/']: return XPath(path) else: raise ValueError("Couldn't identify the path type for {}".format(path))
def iter_map_xml_document_to_dict(xml_document: Path, xml_namespaces: Dict = None, json: Optional[Dict] = None, ignore_empty: bool = True) -> Iterator[Union[Dict, List]]: """ Generator that iteratively maps each node encountered in the input xml_document. It will infer the output type for each node. :param xml_document: A Path to the XML document that is to be converted. :param xml_namespaces: A dictionary defining the XML namespaces with namespace shortname as keys and the full namespace name as values. Follows the xml standard library convention for XML namespaces. :param json: An input dictionary into which the XML document is to be mapped. Defaults to an empty dictionary if none given. :param ignore_empty: A boolean indicating if missing XML Nodes should be ignored. :return: Yields a json serializable dictionary or list """ json = json or {} xml_etree = xml_parse(str(xml_document)) # type: ElementTree root = xml_etree.getroot() root_xpath = XPath(xml_etree.getpath(root)) all_elements = xml_etree.iterfind('//*') # type: Iterable[ElementTree] for element in all_elements: ns_map = element.nsmap element_path = xml_etree.getpath(element) element_xpath = XPath(element_path) element_xpath.shorten_namespaces(ns_map, in_place=True).relative_to(root_xpath, in_place=True) attrib_paths = (XPath(f'{element_path}/@{attrib_name}') for attrib_name, _ in element.attrib.items()) for attrib in attrib_paths: attrib.shorten_namespaces(ns_map, in_place=True) attrib.relative_to(root_xpath, in_place=True) yield attrib for node in generate_nodes(xml_etree, xml_namespaces): jsonize_mapping = node.to_jsonize(attributes='_') node_map: XMLNodeToJSONNode = parse_node_map(jsonize_mapping, transformations=[]) node_map.map(xml_etree, json, xml_namespaces=xml_namespaces, ignore_empty=ignore_empty) yield json
def test_ignore_namespaces(self): xpath = XPath('/adrmsg:ADRMessage/adrmsg:hasMember/aixm:Route/@gml:id') reference = JSONPath('$.ADRMessage.hasMember.Route.@id') self.assertEqual(reference, xpath.to_json_path(attributes='@', with_namespaces=False))
def test_relative_path(self): xpath = XPath('./adrmsg:ADRMessage/adrmsg:hasMember/aixm:Route/@gml:id') reference = JSONPath('@.adrmsg:ADRMessage.adrmsg:hasMember.aixm:Route.@gml:id') self.assertEqual(reference, xpath.to_json_path(attributes='@', with_namespaces=True))
def test_make_relative_path(self): with self.subTest(): xpath = XPath('/root/element[10]/subelement/subsubelement[1]/@attribute') parent = XPath('/root/element[10]') reference = XPath('./subelement/subsubelement[1]/@attribute') with self.subTest(): self.assertEqual(xpath.relative_to(parent, in_place=False), reference) with self.subTest(): xpath.relative_to(parent, in_place=True) self.assertEqual(xpath, reference) with self.subTest(): xpath = XPath('/root/element[10]/subelement/subsubelement[1]') parent = xpath reference = XPath('.') with self.subTest(): self.assertEqual(xpath.relative_to(parent, in_place=False), reference) with self.subTest(): xpath.relative_to(parent, in_place=True) self.assertEqual(xpath, reference)
def test_split(self): absolute_attribute_xpath = XPath('/ns:root/nss:element/@nss:attribute') relative_element_xpath = XPath('./element/subelement') with self.subTest(): # TODO: Evaluate if the numbering convention is appropriate self.assertEqual(absolute_attribute_xpath.split(1), (XPath(''), XPath('./ns:root/nss:element/@nss:attribute'))) self.assertEqual(absolute_attribute_xpath.split(2), (XPath('/ns:root'), XPath('./nss:element/@nss:attribute'))) with self.subTest(): self.assertEqual(relative_element_xpath.split(1), (XPath('.'), XPath('./element/subelement'))) self.assertEqual(relative_element_xpath.split(2), (XPath('./element'), XPath('./subelement'))) with self.subTest(): with self.assertRaises(ValueError): relative_element_xpath.split(-1)
def test_descendance(self): with self.subTest(): ancestor = XPath('/root/element') descendant = XPath('/root/element/subelement/leaf/@attribute') self.assertTrue(descendant.is_descendant_of(ancestor)) with self.subTest(): ancestor = XPath('/root/element') not_descendant = XPath('/root/otherElement/subelement/leaf') self.assertFalse(not_descendant.is_descendant_of(ancestor)) with self.subTest(): ancestor = XPath('/root/element') not_descendant = XPath('/root/elemental') self.assertFalse(not_descendant.is_descendant_of(ancestor))