-
Notifications
You must be signed in to change notification settings - Fork 5
Expand file tree
/
Copy pathxml_using_xpath.py
More file actions
138 lines (114 loc) · 4.83 KB
/
Copy pathxml_using_xpath.py
File metadata and controls
138 lines (114 loc) · 4.83 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
"""Demo of extracting XML data using XPath with both stdlib xml.etree and lxml.
Runs the same set of queries against a product catalog XML file using both
engines side by side, showing where stdlib falls short (e.g. contains(), text()).
Based on a post from https://raccoon.ninja
Sample XML source: https://www.service-architecture.com/articles/object-oriented-databases/xml_file_for_complex_data.html
"""
from pathlib import Path
from xml.etree import ElementTree as ET_xml
from lxml import etree as ET_lxml
def get_root(xml_filename: str, engine: str):
"""Parse an XML file and return the root element.
Args:
xml_filename: Path to the XML file.
engine: Either "xml" (stdlib) or "lxml".
Returns:
The root element, or None on failure.
"""
try:
if engine == "xml":
return ET_xml.parse(xml_filename).getroot()
elif engine == "lxml":
return ET_lxml.parse(xml_filename).getroot()
else:
raise ValueError(f"Unknown engine '{engine}'. Expected: xml or lxml.")
except FileNotFoundError:
print(f"FATAL ERROR: XML file not found! (Filename: {xml_filename})")
return None
except Exception as exc:
print(f"Unexpected error: {exc}")
return None
def example_01(root, engine_name: str) -> None:
"""Get the GENDER attribute from each catalog_item."""
try:
print("Looking for the GENDER for each CATALOG_ITEM inside the CATALOG...")
for i, item in enumerate(root.findall("product/catalog_item")):
print(f"\t{i}: {item.attrib['gender']}")
print(f"--| Success! [Engine: {engine_name}]")
except Exception:
print(f"--| Failed! [Engine: {engine_name}]")
def example_02(root, engine_name: str) -> None:
"""Get the text content of item_number elements."""
try:
print("Looking for the ITEM NUMBER for each CATALOG_ITEM inside the CATALOG...")
for i, item in enumerate(root.findall("product/catalog_item/item_number")):
print(f"\t{i}: {item.text}")
print(f"--| Success! [Engine: {engine_name}]")
except Exception:
print(f"--| Failed! [Engine: {engine_name}]")
def example_03(root, engine_name: str) -> None:
"""Get color swatches for Medium products (grouped by catalog_item)."""
try:
print("Looking for all COLOR_SWATCHES for MEDIUM products...")
for i, item in enumerate(
root.findall("product/catalog_item/size[@description='Medium']")
):
swatches = [s.text for s in item.findall("color_swatch")]
print(f"\t{i} Color swatches for MEDIUM: {', '.join(swatches)}")
print(f"--| Success! [Engine: {engine_name}]")
except Exception:
print(f"--| Failed! [Engine: {engine_name}]")
def example_03b(root, engine_name: str) -> None:
"""Get color swatches for Medium products (flat list)."""
try:
print("Looking for all COLOR_SWATCHES for MEDIUM products...")
for i, item in enumerate(
root.findall(
"product/catalog_item/size[@description='Medium']/color_swatch"
)
):
print(f"\t{i} Color: {item.text}")
print(f"--| Success! [Engine: {engine_name}]")
except Exception:
print(f"--| Failed! [Engine: {engine_name}]")
def example_04(root, engine_name: str) -> None:
"""Find Black color swatches for Large and Extra Large products (lxml only).
Uses contains() and text() XPath functions which are not supported by
the stdlib xml.etree module.
"""
try:
print("Looking for black COLOR_SWATCHES of LARGE and EXTRA LARGE products.")
xpath = (
"product/catalog_item"
"/size[contains(@description, 'Large')]"
"/color_swatch[text()='Black']"
)
for i, item in enumerate(root.xpath(xpath)):
print(
f"\t{i}: Image: {item.attrib['image']}, "
f"Tag: {item.tag}, Text: {item.text}"
)
print(f"--| Success! [Engine: {engine_name}]")
except Exception:
print(f"--| Failed! [Engine: {engine_name}]")
if __name__ == "__main__":
engines = ["xml", "lxml"]
xml_file = str(Path(__file__).parent / "samples" / "prod_catalog.xml")
for eng in engines:
print("=" * 71)
print(f"TESTING ENGINE: {eng}")
root = get_root(xml_filename=xml_file, engine=eng)
if root is None:
print(f"Skipping tests with {eng}. Could not read XML.")
continue
example_01(root=root, engine_name=eng)
print("-" * 71)
example_02(root=root, engine_name=eng)
print("-" * 71)
example_03(root=root, engine_name=eng)
print("-" * 71)
example_03b(root=root, engine_name=eng)
print("-" * 71)
example_04(root=root, engine_name=eng)
print()
print("All done!")