Working with Custom XML#

A .docx file is an Open Packaging Conventions package. Custom XML parts store application-specific XML inside its customXml directory without placing that data in the visible document body.

Warning

Some applications that edit .docx files may remove Custom XML parts. Test the complete editing workflow used by your application. For simple scalar metadata, Custom Properties may be a more interoperable choice.

Creating a part#

Create a Custom XML part from a well-formed XML string:

from docx import Document

document = Document()
customers = document.part.add_custom_xml_part(
    '<customers xmlns="urn:example:customers"><customer id="1">Ada</customer></customers>'
)
document.save("customers.docx")

When no file_name is supplied, python-docx-oss selects the next available package name matching /customXml/itemN.xml. For example, if item1.xml and item3.xml already exist, the new part is named item2.xml.

An explicit name can be supplied without the .xml suffix:

audit = document.part.add_custom_xml_part(
    "<audit><event>created</event></audit>",
    file_name="audit",
)

The resulting part name is /customXml/audit.xml. A ValueError is raised if that part name already exists.

Finding parts#

document.part.custom_xml_parts returns all Custom XML parts related to the main document. Select a part by its package name or root element rather than by list position:

parts_by_name = {
    str(part.partname): part
    for part in document.part.custom_xml_parts
}
customers = parts_by_name["/customXml/item1.xml"]

print(customers.tag)
print(customers.attrib)
print(customers.items)

Updating child elements#

items contains the child elements of the part’s root element. These are lxml elements, so their text and attributes can be updated directly:

customer = customers.items[0]
customer.text = "Grace"
customer.attrib["status"] = "active"

Use Clark notation when adding an element in a namespace:

customers.add_item(
    "{urn:example:customers}customer",
    "Linus",
    id="2",
)

Deleting child elements#

Delete a child by passing the element itself:

customer = customers.items[0]
customers.delete_item(customer)

The indexes in items can change after deletion. Removing an entire Custom XML part is not currently provided by this API.

Existing relationships to Custom XML property parts, such as itemPropsN.xml, are preserved during load/save round trips.