DoclingDocument Hierarchization (_hierarchize)#
_hierarchize() is an internal transformation method on DoclingDocument that restructures a flat list of document body items into a heading-based subtree hierarchy. It is the inverse of _flatten(), which collapses the tree back to a single level.
What It Does#
After a document is assembled (e.g., by loading a serialized file), body items may all sit as siblings directly under doc.body. _hierarchize() walks those items and re-parents each one under the deepest heading ancestor whose level is strictly less than the item's own level — giving the document a semantically meaningful tree structure.
Heading level semantics used by the method :
| Node type | Level value |
|---|---|
TitleItem | 0 |
SectionHeaderItem | node.level (1–N) |
doc.body (root) | -1 |
| Any other item | None (no heading semantics) |
Algorithm#
The method uses a single outer while loop that restarts traversal whenever the tree is modified :
- Track active section roots — a
section_root_by_leveldict maps each heading level to its node, seeded with{-1: self.body}. - Resume-from mechanism — a
resume_nodepointer skips already-processed items after a tree edit, avoiding a full restart . - Skip FloatingItem descendants — items inside
FloatingItemcontainers (tables, pictures, key-values, forms) are skipped entirely to preserve their structural integrity . - Find target root — for each non-floating item, the highest tracked level that is still strictly less than the item's own level is selected as the target parent .
- Move if needed — if the item is not already a descendant of the target root,
_move_subtree()re-parents it and traversal resumes from the moved item . - Register headings — when a heading is encountered, all same-or-lower-priority heading entries (same level or deeper) are evicted from the tracking dict, and the new heading is registered .
Floating Item Preservation#
FloatingItem containers — TableItem, PictureItem, KeyValueItem, FormItem — have their own internal parent-child structure. Any item whose ancestor chain includes a FloatingItem is left untouched . This is the key correctness invariant: heading-based re-parenting must never reach inside these containers.
Helper Methods#
| Method | Lines | Purpose |
|---|---|---|
_get_heading_level(node) | 1211–1220 | Returns heading level or None |
_is_descendant_of(node1, node2) | 1222–1229 | Checks ancestry by walking parent refs |
_move_subtree(old_subroot, new_subroot) | 1193–1209 | Re-parents a subtree and updates both ends of the link |
When It Is Called#
_hierarchize() is a private method (_ prefix). Based on the codebase, it is invoked post-deserialization to impose heading structure on a freshly loaded flat document, and can also be called explicitly in tests or pipelines that need to enforce the heading-based tree layout. The complementary _flatten() call can precede it when a full re-hierarchization is needed.
Primary source file: docling_core/types/doc/document.py — lines 1193–1283.