A minimal XML parser for structured output from LLM
Project description
XML Parser for Structured LLM Output
General Description
This parser implements a stack-based algorithm to analyze XML hierarchies, tracking parent-child relationships and structure depth. It offers flexible configurations for extracting specific tags (both single and lists) at defined depths, with a rigorous validation system. The implementation is optimized for small XML files (typically a few KB) with simple hierarchical structures (maximum 3-4 levels of depth).
Core Features
The parser handles:
- Critical Errors: missing tags, unauthorized duplicates, empty lists
- Non-Blocking Warnings: nested tags in unexpected structures, mixed XML/text content
- Untagged Text Handling: preserves text not enclosed in XML tags
- Case-Insensitive Tag Search: tag names are matched regardless of case.
- Original Text Capitalization: output text retains the exact capitalization from the input.
Key Technical Features
- Exclusive use of Python standard libraries (no external dependencies)
- Case-insensitive only for tag names during search
- Preservation of original text, including capitalization and formatting
- Integrated logging system for warnings and errors
Use Cases and Behaviors
Important Note: The ParseResult structure is flat. All configured tags, including those defined as "children" in the configuration, are accessible as first-level attributes of the result object. There is no nested hierarchical structure.
✅ Correctly Handled Cases
Elements extracted without warnings or errors.
1. Single Element at Depth 0
XML:
<thinking>This is a test</thinking>
Config: {'thinking': 'single'}
Output:
result.thinking → "This is a test"
2. List with 2+ Elements
XML:
<step>A</step>
<step>B</step>
<step>C</step>
Config: {'step': 'list'}
Output:
result.step → ["A", "B", "C"]
3. Explicitly Configured Hierarchy
XML:
<exercise>
<question>What is 2+2?</question>
<answer>The answer is 4</answer>
</exercise>
Config:
config = {
'exercise': {
'type': 'single',
'children': {
'question': 'single',
'answer': 'single'
}
}
}
Output:
result.exercise → "<question>What is 2+2?</question><answer>The answer is 4</answer>"
result.question → "What is 2+2?"
result.answer → "The answer is 4"
4. Correct Untagged Text Extraction
LLM Input:
<thinking>Analysis</thinking>
Answer: 42
After Pre-Processing:
<root>
<thinking>Analysis</thinking>
Answer: 42
</root>
Note: Preprocessing always adds an artificial root tag, since LLM output often does not include a valid root tag. The root tag is shown here only to illustrate the handling of unlabeled text.
Output:
result.thinking → "Analysis"
result.untagged → "Answer: 42"
⚠️ Warnings (Extraction with Notification)
Elements extracted with warnings in logs and accessible via ParseResult.warnings
1. Nested Tags in Single Element
XML:
<thinking>Use <formula>E=mc²</formula></thinking>
Config: {'thinking': 'single'}
Output:
result.thinking → "Use <formula>E=mc²</formula>"
# Log and warnings: "Unconfigured tag <formula> found inside 'thinking'"
2. Unconfigured Tag in List
XML:
<step>
A<detail>some info</detail>
</step>
<step>B</step>
Config: {'step': 'list'}
Output:
result.step → ["A<detail>some info</detail>", "B"]
# Log and warnings: "Unconfigured tag <detail> found inside 'step'"
3. List with 1 Element
XML:
<step>Single step</step>
Config: {'step': 'list'}
Output:
result.step → ["Single step"]
# Log and warnings: "List <step> contains only 1 element."
❌ Blocking Errors
Interrupt execution by raising specific exceptions.
1. Missing Single Element
XML: <answer>42</answer>
Config: {'thinking': 'single'}
Error:
XMLStructureError: Tag <thinking> not found (single required).
2. Too Many Single Elements
XML:
<answer>42</answer>
<answer>43</answer>
Config: {'answer': 'single'}
Error:
XMLStructureError: Multiple <answer> found, but 'single' is required.
3. Empty List
XML: <answer>stuff</answer>
Config: {'steps': 'list'}
Error:
XMLStructureError: List <steps> is empty (1+ elements required).
4. Malformed XML
XML:
<thinking>Test<thinking>
or
<a><b></a></b>
Error:
XMLFormatError: Unclosed tags remain. Malformed XML structure.
or
XMLFormatError: Mismatched tags: opened <b>, but closed </a>
Unhandled Cases
The parser is designed to be minimalist and efficiently handle simple XML output from LLMs. The following cases are not supported:
-
Namespaces
- ❌ Invalid:
<ns:thinking>Test</ns:thinking>
- ❌ Invalid:
-
Tags with Special Characters
- ✅ Valid:
<distro_linux>Test</distro_linux>(underscore supported) - ❌ Invalid:
<foo-bar>Test</foo-bar> - ❌ Invalid:
<foo bar>Test</foo bar>
- ✅ Valid:
-
CDATA and Complex Content
- CDATA sections not interpreted
- XML entities not processed
- ❌ Not handled:
<thinking><![CDATA[<test>]]></thinking>
-
XML Attributes
- Tag attributes ignored
- ❌ Not handled:
<tag attr="val">Test</tag>
Implementation Details
Execution Modes
-
Normal Mode (Default,
strict_mode=False)- Warnings logged but non-blocking
- Only errors generate exceptions
- Output preserved even with warnings
-
Strict Mode (
strict_mode=True)- All warnings become blocking errors
- Maximum validation rigor
Input Handling
- Size: Optimized for small XML files (10-20 KB)
- Depth: Efficient support up to 3-4 levels
- Case Sensitivity:
- Tags: case-insensitive during search
- Content: preserved exactly as in input
Pre-Processing
- Automatic removal of XML comments
- Automatic root tag addition
- Basic input normalization
Technical Requirements
- Python: Version 3.7 or higher
- Dependencies: Python standard library only
- Encoding: UTF-8 for input/output
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file llm_xml_parser-0.1.3.tar.gz.
File metadata
- Download URL: llm_xml_parser-0.1.3.tar.gz
- Upload date:
- Size: 12.9 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: poetry/1.8.5 CPython/3.13.1 Linux/6.12.10-arch1-1
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
9148404e43a685dc8a9d24b098415d4d6bc208d96d8f3d9d8964a51a2c25a2b2
|
|
| MD5 |
c1398a7c3c3f12f99e3d205ead728777
|
|
| BLAKE2b-256 |
d3adbe87f7cddfe46b90fb6ede3b7662d748b666397c9666ec52769d36aed626
|
File details
Details for the file llm_xml_parser-0.1.3-py3-none-any.whl.
File metadata
- Download URL: llm_xml_parser-0.1.3-py3-none-any.whl
- Upload date:
- Size: 14.8 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: poetry/1.8.5 CPython/3.13.1 Linux/6.12.10-arch1-1
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
93e3014c0844a326fad1b7a7bc56647269af71c3b2dca46509134790bed37760
|
|
| MD5 |
7d4d63cd1966c4bab0ae5d689ded0fc6
|
|
| BLAKE2b-256 |
4b5302b0fe860ce784273885f811dcd6aee186ec6a70422a72500ca3b9980ae1
|