XML Utility tools with Pydantic and Typer
Project description
xmlu
XML Utility - Transform XML files into type-safe Pydantic models with automatic type inference and structure detection.
Features
- Automatic Type Inference: Detects
str,int, andbooltypes from XML values - Smart Field Detection: Identifies required vs. optional fields based on occurrence patterns
- Nested Model Support: Recognizes and generates nested models for XML elements with
Name/Valueattributes - Pythonic Naming: Converts XML tags to
snake_casewith field aliases to preserve original names - CLI & API: Use as a command-line tool or import as a Python library
- Type-Safe: Generated models use Pydantic v2 for runtime validation
- Rich Output: Beautiful terminal interface with progress information
Installation
# Using uv (recommended)
uv pip install xmlu
# Using pip
pip install xmlu
Quick Start
CLI Usage
# Generate models from XML file
xmlu generate schedule.xml --parent Event
# Specify custom output file
xmlu generate data.xml --parent Item --output models.py
# Verbose mode for detailed progress
xmlu generate schedule.xml --parent Event --verbose
# Show version
xmlu version
Python API
from xmlu import generate_pydantic_models, create_models_file
# Generate models
models = generate_pydantic_models("schedule.xml", parent_element="Event")
Event, Fields = models
# Create a models file
output_file = create_models_file("schedule.xml", list(models))
print(f"Generated: {output_file}")
# Use the generated models
event = Event(
is_fixed=True,
fields=Fields(duration="01:00:00", enabled=True)
)
How It Works
xmlu analyzes your XML structure and generates Pydantic models with intelligent defaults:
Input XML:
<Schedule>
<Event>
<EventId>1</EventId>
<IsFixed>True</IsFixed>
<Fields>
<Parameter Name="Duration" Value="01:00:00" />
<Parameter Name="Device" Value="SM-1" />
</Fields>
</Event>
<Event>
<EventId>2</EventId>
<IsFixed>False</IsFixed>
<Fields>
<Parameter Name="Duration" Value="02:00:00" />
</Fields>
</Event>
</Schedule>
Generated Pydantic Models:
"""Auto-generated Pydantic models from Schedule.xml"""
from pydantic import BaseModel, Field, ConfigDict
from typing import Optional
class Fields(BaseModel):
model_config = ConfigDict(
str_strip_whitespace=True,
validate_assignment=True,
populate_by_name=True,
)
duration: str = Field(alias="Duration")
device: Optional[str] = Field(None, alias="Device") # Optional - not in all Events
class Event(BaseModel):
model_config = ConfigDict(
str_strip_whitespace=True,
validate_assignment=True,
populate_by_name=True,
)
event_id: int = Field(alias="EventId")
is_fixed: bool = Field(alias="IsFixed")
fields: Fields = Field(alias="Fields")
Features in Detail
Type Inference
xmlu automatically infers Python types:
- Boolean:
True,False,0,1→bool - Integer: Numeric strings →
int - String: All other values →
str
Optional Fields
Fields that don't appear in every XML element are marked as Optional[T]:
# Field present in 2 out of 3 events → Optional
device: Optional[str] = None
Nested Structures
XML elements with child elements containing Name and Value attributes are automatically converted to nested models:
<Fields>
<Parameter Name="Duration" Value="01:00:00" />
<Parameter Name="Device" Value="SM-1" />
</Fields>
Becomes:
class Fields(BaseModel):
duration: str
device: str
Snake Case Conversion
XML tags are converted to Pythonic snake_case while preserving original names via field aliases:
# XML: <EventId>123</EventId>
event_id: int = Field(alias="EventId")
# Both work for parsing:
Event(event_id=123)
Event(EventId=123)
CLI Commands
generate
Generate Pydantic models from an XML file.
xmlu generate [FILE] [OPTIONS]
Options:
--parent, -p TEXT: XML tag to use as parent model (default: "Event")--output, -o PATH: Output file path (default: auto-generated)--verbose, -v: Show detailed progress information--help: Show help message
Examples:
# Basic usage
xmlu generate schedule.xml
# Custom parent element
xmlu generate data.xml --parent CustomElement
# Specify output file
xmlu generate data.xml -o app/models.py
# Verbose output
xmlu generate schedule.xml --verbose
version
Show version information.
xmlu version
API Reference
generate_pydantic_models(file_path, parent_element="Event")
Generate Pydantic models from an XML file.
Parameters:
file_path(str): Path to the XML fileparent_element(str): XML tag name to use as parent model
Returns:
tuple[type[BaseModel], ...]: Tuple of Pydantic models (parent first, then nested models)
Example:
models = generate_pydantic_models("schedule.xml", "Event")
Event, Fields = models
create_models_file(file_path, models)
Write Pydantic models to a Python file.
Parameters:
file_path(str): Path to source XML file (used for naming output)models(list[type[BaseModel]]): List of Pydantic models to write
Returns:
str: Name of the generated file
Example:
output_file = create_models_file("schedule.xml", list(models))
# Returns: "schedule_models.py"
Utility Functions
convert_to_snake_case(name)
Convert any string to snake_case.
from xmlu import convert_to_snake_case
convert_to_snake_case("CamelCase") # "camel_case"
convert_to_snake_case("HTTPServer") # "http_server"
convert_to_snake_case("kebab-case") # "kebab_case"
convert_to_pascal_case(name)
Convert any string to PascalCase.
from xmlu import convert_to_pascal_case
convert_to_pascal_case("snake_case") # "SnakeCase"
convert_to_pascal_case("HTTPServer") # "HTTPServer"
convert_to_pascal_case("kebab-case") # "KebabCase"
Development
Setup
# Clone the repository
git clone https://github.com/MichielMe/xmlu.git
cd xmlu
# Install dependencies with uv
uv sync
# Run tests
make test
Running Tests
# Run all tests with pytest
make test
# Or directly with uv
uv run pytest -v
Project Structure
xmlu/
├── src/
│ └── xmlu/
│ ├── __init__.py # Public API exports
│ ├── main.py # CLI application
│ ├── convert_to_pydantic.py # Core model generation
│ └── utils.py # String utilities & type inference
├── tests/
│ ├── test_generator.py # Model generation tests
│ └── test_string_utils.py # Utility function tests
├── pyproject.toml # Project configuration
└── README.md
Configuration
Generated models include sensible defaults:
model_config = ConfigDict(
str_strip_whitespace=True, # Auto-strip whitespace
validate_assignment=True, # Validate on field assignment
populate_by_name=True, # Accept both snake_case and original names
)
Requirements
- Python 3.12+
- lxml >= 6.0.2
- pydantic >= 2.12.3
- typer >= 0.20.0
License
MIT License - see LICENSE file for details.
Author
Michiel Meire
- Email: michiel.meire1@gmail.com
- GitHub: @MichielMe
Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
Roadmap
- JSON output format support
- XML Schema (XSD) validation
- Support for XML attributes (not just elements)
- List/array type detection for repeated elements
- Custom type mapping configuration
- Watch mode for automatic regeneration
Acknowledgments
Built with:
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 xmlu-0.1.0.tar.gz.
File metadata
- Download URL: xmlu-0.1.0.tar.gz
- Upload date:
- Size: 33.1 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.5.10
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
14df24c4f59bb7b8523f2e7e71499b9f984994e0fda7393e7c1c97a269407bdc
|
|
| MD5 |
ea68c1cdd3b34d0478b202b9c6baa833
|
|
| BLAKE2b-256 |
aff54ceb3e0010d261a5fcbd1fb76ea7d435f854927945f1248b25a27a3f5302
|
File details
Details for the file xmlu-0.1.0-py3-none-any.whl.
File metadata
- Download URL: xmlu-0.1.0-py3-none-any.whl
- Upload date:
- Size: 12.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.5.10
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
a0bba0e2a8d4567da64f2b700f66b3965d007b47b77cd14685fe469dbce15d72
|
|
| MD5 |
6ac9fe815b2dfa5e8ced253badda00be
|
|
| BLAKE2b-256 |
633b928bb73eb7841a361eb815b6294e3e03cff0914ca0ef2ced153bb1cd8eba
|