0
# Object-Oriented XML API
1
2
Pythonic XML processing that automatically converts XML elements to native Python objects with proper data types. The objectify module provides intuitive attribute-based access to XML content while maintaining full XML structure and namespace support.
3
4
## Capabilities
5
6
### Document Parsing
7
8
Parse XML documents into objectified trees with automatic type conversion.
9
10
```python { .api }
11
def parse(source, parser=None, base_url=None):
12
"""
13
Parse XML document into objectified tree.
14
15
Args:
16
source: File path, URL, or file-like object
17
parser: ObjectifyElementClassLookup-enabled parser (optional)
18
base_url: Base URL for resolving relative references (optional)
19
20
Returns:
21
ObjectifiedElement: Root element with objectified children
22
"""
23
24
def fromstring(text, parser=None, base_url=None):
25
"""
26
Parse XML string into objectified element.
27
28
Args:
29
text: str or bytes containing XML content
30
parser: ObjectifyElementClassLookup-enabled parser (optional)
31
base_url: Base URL for resolving relative references (optional)
32
33
Returns:
34
ObjectifiedElement: Root element with objectified children
35
"""
36
37
def XML(text, parser=None, base_url=None):
38
"""Parse XML string into objectified element with validation."""
39
```
40
41
### Objectified Elements
42
43
Elements that provide Python object-like access to XML content with automatic type conversion.
44
45
```python { .api }
46
class ObjectifiedElement:
47
"""
48
XML element with Python object-like attribute access and type conversion.
49
"""
50
51
# Attribute access - returns child elements or text content
52
def __getattr__(self, name):
53
"""Access child elements as attributes."""
54
55
def __setattr__(self, name, value):
56
"""Set child element content or create new elements."""
57
58
def __delattr__(self, name):
59
"""Remove child elements."""
60
61
# List-like access for multiple children with same name
62
def __getitem__(self, index):
63
"""Access children by index."""
64
65
def __len__(self):
66
"""Number of child elements."""
67
68
def __iter__(self):
69
"""Iterate over child elements."""
70
71
# Dictionary-like access for attributes
72
def get(self, key, default=None):
73
"""Get XML attribute value."""
74
75
def set(self, key, value):
76
"""Set XML attribute value."""
77
78
# Python value conversion
79
def __str__(self):
80
"""String representation of element text content."""
81
82
def __int__(self):
83
"""Integer conversion of element text."""
84
85
def __float__(self):
86
"""Float conversion of element text."""
87
88
def __bool__(self):
89
"""Boolean conversion of element text."""
90
91
# XML methods (inherited from Element)
92
def xpath(self, path, **kwargs):
93
"""XPath evaluation on objectified tree."""
94
95
def find(self, path, namespaces=None):
96
"""Find child elements."""
97
98
def findall(self, path, namespaces=None):
99
"""Find all child elements."""
100
101
class ObjectifiedDataElement:
102
"""Objectified element containing typed data."""
103
104
@property
105
def pyval(self):
106
"""Python value with automatic type conversion."""
107
108
@pyval.setter
109
def pyval(self, value):
110
"""Set Python value with type annotation."""
111
```
112
113
### Data Element Classes
114
115
Specialized element classes for different Python data types.
116
117
```python { .api }
118
class StringElement(ObjectifiedDataElement):
119
"""Element containing string data."""
120
121
class IntElement(ObjectifiedDataElement):
122
"""Element containing integer data."""
123
124
class FloatElement(ObjectifiedDataElement):
125
"""Element containing floating-point data."""
126
127
class BoolElement(ObjectifiedDataElement):
128
"""Element containing boolean data."""
129
130
class NoneElement(ObjectifiedDataElement):
131
"""Element representing None/null value."""
132
133
class NumberElement(ObjectifiedDataElement):
134
"""Element containing numeric data (int or float auto-detected)."""
135
136
def DataElement(_value, _pytype=None, _xsi=None, **kwargs):
137
"""
138
Create data element with specified type.
139
140
Args:
141
_value: Python value to store
142
_pytype: Python type name override
143
_xsi: XML Schema instance type
144
**kwargs: Additional element attributes
145
146
Returns:
147
ObjectifiedDataElement: Typed data element
148
"""
149
```
150
151
### Element Creation
152
153
Factory classes and functions for creating objectified elements.
154
155
```python { .api }
156
class ElementMaker:
157
"""Factory for creating objectified elements with namespace support."""
158
159
def __init__(self, namespace=None, nsmap=None, makeelement=None,
160
typemap=None, **kwargs):
161
"""
162
Create element factory.
163
164
Args:
165
namespace: Default namespace URI
166
nsmap: Namespace prefix mapping
167
makeelement: Custom element factory function
168
typemap: Type mapping for value conversion
169
**kwargs: Default attributes for created elements
170
"""
171
172
def __call__(self, tag, *children, **kwargs):
173
"""Create element with tag, children, and attributes."""
174
175
def __getattr__(self, tag):
176
"""Create element factory function for specific tag."""
177
178
# Default element maker instance
179
E = ElementMaker()
180
181
class ObjectPath:
182
"""
183
Object path for navigating objectified XML trees.
184
"""
185
186
def __init__(self, path):
187
"""
188
Create object path from dot-separated string.
189
190
Args:
191
path: Dot-separated path like "root.child.grandchild"
192
"""
193
194
def find(self, root):
195
"""Find element at path in objectified tree."""
196
197
def setattr(self, root, value):
198
"""Set value at path, creating elements as needed."""
199
200
def hasattr(self, root):
201
"""Test if path exists in tree."""
202
```
203
204
### Type Annotation
205
206
Functions for managing Python type information in XML.
207
208
```python { .api }
209
def annotate(element_or_tree, tag=None, empty_pytype=None,
210
ignore_old=False, ignore_xsi=False, empty_type=None):
211
"""
212
Add Python type annotations to elements based on content.
213
214
Args:
215
element_or_tree: Element or tree to annotate
216
tag: Only annotate elements with this tag
217
empty_pytype: Type to use for empty elements
218
ignore_old: Ignore existing pytype annotations
219
ignore_xsi: Ignore existing xsi:type annotations
220
empty_type: Type class for empty elements
221
"""
222
223
def deannotate(element_or_tree, pytype=True, xsi=True, xsi_nil=True,
224
cleanup_namespaces=False):
225
"""
226
Remove type annotations from elements.
227
228
Args:
229
element_or_tree: Element or tree to process
230
pytype: Remove pytype attributes
231
xsi: Remove xsi:type attributes
232
xsi_nil: Remove xsi:nil attributes
233
cleanup_namespaces: Remove unused namespace declarations
234
"""
235
236
def pyannotate(element_or_tree, ignore_old=False, ignore_xsi=False,
237
empty_type=None):
238
"""Add Python-specific type annotations."""
239
240
def xsiannotate(element_or_tree):
241
"""Add XML Schema instance type annotations."""
242
```
243
244
### Parser Configuration
245
246
Specialized parsers for objectify functionality.
247
248
```python { .api }
249
class ObjectifyElementClassLookup:
250
"""Element class lookup that creates objectified elements."""
251
252
def __init__(self, tree_class=None, empty_data_class=None):
253
"""
254
Create objectify element class lookup.
255
256
Args:
257
tree_class: Class for tree/document elements
258
empty_data_class: Class for empty data elements
259
"""
260
261
def makeparser(**kwargs):
262
"""
263
Create parser configured for objectify processing.
264
265
Args:
266
**kwargs: Parser configuration options
267
268
Returns:
269
XMLParser: Parser with ObjectifyElementClassLookup
270
"""
271
272
def set_default_parser(parser):
273
"""Set default parser for objectify module."""
274
```
275
276
### Utility Functions
277
278
Helper functions for working with objectified trees.
279
280
```python { .api }
281
def dump(element_or_tree):
282
"""Print debug representation of objectified tree."""
283
284
def enable_recursive_str(enabled=True):
285
"""
286
Enable/disable recursive string representation.
287
288
Args:
289
enabled: Enable recursive str() for nested elements
290
"""
291
292
def set_pytype_attribute_tag(attribute_tag=None):
293
"""
294
Configure attribute name for Python type information.
295
296
Args:
297
attribute_tag: Attribute name (None for default)
298
"""
299
300
def pytypename(obj):
301
"""
302
Get Python type name for object.
303
304
Args:
305
obj: Python object
306
307
Returns:
308
str: Type name string
309
"""
310
311
def getRegisteredTypes():
312
"""
313
Get list of registered Python types.
314
315
Returns:
316
list: Registered type classes
317
"""
318
```
319
320
### Type Management
321
322
Classes for managing Python type information.
323
324
```python { .api }
325
class PyType:
326
"""Python type annotation handler."""
327
328
def __init__(self, name, type_check, type_class, stringify=None):
329
"""
330
Register Python type.
331
332
Args:
333
name: Type name string
334
type_check: Function to test if value matches type
335
type_class: Element class for this type
336
stringify: Function to convert value to string
337
"""
338
339
@property
340
def name(self) -> str:
341
"""Type name."""
342
343
@property
344
def xmlSchemaTypes(self) -> list:
345
"""Associated XML Schema types."""
346
```
347
348
## Usage Examples
349
350
### Basic Object-Oriented Access
351
352
```python
353
from lxml import objectify
354
355
# Parse XML with automatic type conversion
356
xml_data = '''<?xml version="1.0"?>
357
<catalog>
358
<book id="1">
359
<title>Python Programming</title>
360
<author>John Smith</author>
361
<year>2023</year>
362
<price>29.99</price>
363
<available>true</available>
364
<chapters>12</chapters>
365
</book>
366
<book id="2">
367
<title>Web Development</title>
368
<author>Jane Doe</author>
369
<year>2022</year>
370
<price>34.95</price>
371
<available>false</available>
372
<chapters>15</chapters>
373
</book>
374
</catalog>'''
375
376
root = objectify.fromstring(xml_data)
377
378
# Access as Python attributes with automatic type conversion
379
print(root.book[0].title) # "Python Programming" (string)
380
print(root.book[0].year) # 2023 (integer)
381
print(root.book[0].price) # 29.99 (float)
382
print(root.book[0].available) # True (boolean)
383
print(root.book[0].chapters) # 12 (integer)
384
385
# Access XML attributes
386
print(root.book[0].get('id')) # "1"
387
388
# Iterate over multiple elements
389
for book in root.book:
390
print(f"{book.title} ({book.year}): ${book.price}")
391
392
# Modify content
393
root.book[0].price = 24.99
394
root.book[0].sale = True # Creates new element
395
396
print(objectify.dump(root))
397
```
398
399
### Creating Objectified XML
400
401
```python
402
from lxml import objectify
403
404
# Create root element
405
root = objectify.Element("products")
406
407
# Add child elements with data
408
root.product = objectify.Element("product", id="1")
409
root.product.name = "Widget"
410
root.product.price = 19.99
411
root.product.in_stock = True
412
root.product.categories = objectify.Element("categories")
413
root.product.categories.category = ["Electronics", "Gadgets"]
414
415
# Add another product
416
product2 = objectify.SubElement(root, "product", id="2")
417
product2.name = "Gadget"
418
product2.price = 15.50
419
product2.in_stock = False
420
421
# Convert to string
422
xml_string = objectify.dump(root)
423
print(xml_string)
424
```
425
426
### Using ElementMaker
427
428
```python
429
from lxml import objectify
430
431
# Create custom ElementMaker
432
E = objectify.ElementMaker(annotate=False)
433
434
# Build XML structure
435
doc = E.order(
436
E.id(12345),
437
E.customer(
438
E.name("John Doe"),
439
E.email("john@example.com")
440
),
441
E.items(
442
E.item(
443
E.product("Widget"),
444
E.quantity(2),
445
E.price(19.99)
446
),
447
E.item(
448
E.product("Gadget"),
449
E.quantity(1),
450
E.price(15.50)
451
)
452
),
453
E.total(55.48),
454
E.date("2023-12-07")
455
)
456
457
# Access created structure
458
print(f"Order ID: {doc.id}")
459
print(f"Customer: {doc.customer.name}")
460
print(f"Total: ${doc.total}")
461
462
for item in doc.items.item:
463
print(f"- {item.product}: {item.quantity} @ ${item.price}")
464
```
465
466
### Object Path Navigation
467
468
```python
469
from lxml import objectify
470
471
xml_data = '''
472
<config>
473
<database>
474
<host>localhost</host>
475
<port>5432</port>
476
<credentials>
477
<username>admin</username>
478
<password>secret</password>
479
</credentials>
480
</database>
481
</config>'''
482
483
root = objectify.fromstring(xml_data)
484
485
# Create object paths
486
host_path = objectify.ObjectPath("database.host")
487
creds_path = objectify.ObjectPath("database.credentials")
488
489
# Navigate using paths
490
print(host_path.find(root)) # "localhost"
491
creds = creds_path.find(root)
492
print(f"User: {creds.username}, Pass: {creds.password}")
493
494
# Set values using paths
495
host_path.setattr(root, "prod-server.com")
496
print(host_path.find(root)) # "prod-server.com"
497
```
498
499
### Type Annotation Control
500
501
```python
502
from lxml import objectify
503
504
xml_data = '''
505
<data>
506
<number>42</number>
507
<decimal>3.14</decimal>
508
<flag>true</flag>
509
<text>hello</text>
510
</data>'''
511
512
root = objectify.fromstring(xml_data)
513
514
# Check current annotations
515
print("Before annotation:")
516
print(objectify.dump(root))
517
518
# Add type annotations
519
objectify.annotate(root)
520
print("\nAfter annotation:")
521
print(objectify.dump(root))
522
523
# Remove annotations
524
objectify.deannotate(root)
525
print("\nAfter deannotation:")
526
print(objectify.dump(root))
527
528
# Custom type handling
529
objectify.set_pytype_attribute_tag("data-type")
530
objectify.annotate(root)
531
print("\nWith custom type attribute:")
532
print(objectify.dump(root))
533
```