Production-grade JSON to UBL 2.1 XML converter with schema-driven mapping
json2ubl is a production-ready converter that transforms JSON documents into UBL 2.1-compliant XML. It works with all 60+ UBL document types using automatic schema-driven mappingβno hardcoded field definitions required.
- Universal Document Support - Works with all 60+ UBL 2.1 document types (Invoice, CreditNote, Order, DebitNote, etc.)
- Schema-Driven Processing - Automatic field mapping and validation from XSD schemas, no hardcoded rules
- Multi-Page Support - Automatically merges multi-page documents (e.g., multi-page invoices) into valid UBL XML
- Thread-Safe - Built-in concurrency support for batch processing
- Error Resilience - Comprehensive error handling with rollback on partial failures
- Production Ready - Minimal dependencies, extensive logging, optimized for performance
- Flexible Output - Write to disk, return XML strings, or get unmapped fields for validation
- Type-Safe - Full Python type hints and validation with Pydantic
pip install json2ublRequirements:
- Python >= 3.10
- lxml >= 4.9.4
- pydantic >= 2.7.0
- pyyaml >= 6.0.1
- loguru >= 0.7.2
from json2ubl import json_dict_to_ubl_xml
# List of invoices
invoices = [
{
"id": "INV-2026-001",
"issue_date": "2026-01-30",
"due_date": "2026-02-28",
"document_type": 380, # 380 = Invoice
"accounting_supplier_party": {
"party_name": "Acme Corp",
"party_identification": {"id": "123456"}
},
"accounting_customer_party": {
"party_name": "Customer Inc",
},
"invoice_lines": [
{
"id": "1",
"invoiced_quantity": 10,
"invoiced_quantity_unit_code": "EA",
"line_extension_amount": 1000.00
}
]
},
{
"id": "INV-2026-002",
"issue_date": "2026-01-31",
"document_type": 380,
...
}
]
response = json_dict_to_ubl_xml(invoices)
for doc in response["documents"]:
print(f"Converted {doc['id']}")
print(doc["xml"]) # UBL 2.1 XML stringfrom json2ubl import json_file_to_ubl_xml_dict
# JSON file must contain list: [{}, {}]
response = json_file_to_ubl_xml_dict("invoices.json")
print(f"Converted {len(response['documents'])} documents")
for doc in response["documents"]:
print(f" - {doc['id']}: {len(doc['unmapped_fields'])} unmapped fields")
print(doc["xml"])from json2ubl import json_file_to_ubl_xml_files
# JSON file must contain list: [{}, {}]
response = json_file_to_ubl_xml_files(
json_file_path="invoices.json",
output_dir="./output_xml"
)
print(f"Generated {response['summary']['files_created']} XML files")Supported UBL 2.1 document types (numeric codes):
- 380 - Invoice
- 381 - Credit Note
- 382 - Debit Note
- 220 - Order
- 225 - Order Change
- 230 - Order Cancellation
- ... and 55+ more UBL document types
Full list: UBL 2.1 Document Types
Convert list of JSON dicts to UBL 2.1 XML strings in memory.
Args:
list_of_dicts: List of document dicts withdocument_type(numeric code) and schema fieldsconfig_path: Optional path toubl_converter.yaml
Returns:
{
"documents": [
{
"id": "DOC-ID",
"xml": "<ubl:Invoice>...</ubl:Invoice>",
"unmapped_fields": ["custom_field_1"]
}
],
"summary": {
"total_inputs": 2,
"files_created": 0,
"document_types": {"Invoice": 2}
}
}Convert JSON file to UBL 2.1 XML strings (in-memory).
Args:
json_file_path: Path to JSON file containing list:[{}, {}]
Returns: Same as json_dict_to_ubl_xml()
Convert JSON file and write XML files to disk.
Features:
- JSON file must contain list:
[{}, {}] - Auto-detects output directory write permissions
- Rolls back on partial failure
- Atomic file operations with temp file staging
For detailed API documentation with input/output examples and error handling, see API.md
The converter includes comprehensive error handling:
response = json_dict_to_ubl_xml([document])
if response.get("error_response"):
print(f"Error: {response['error_response']}")
else:
for doc in response["documents"]:
print(f"Converted {doc['id']}")
if doc["unmapped_fields"]:
print(f" Unmapped: {doc['unmapped_fields']}")Common Issues:
- Missing
document_typefield β Error with guidance - Invalid
document_typecode β Lists valid codes - Null input fields β Preserved as empty XML elements
- Multi-page documents β Automatically merged (with configurable strategy)
Run tests:
pip install -e .[dev]
pytest tests/ -vTest coverage includes:
- All 60+ UBL document types
- Multi-page document merging
- Error handling and rollback
- Concurrent batch processing
- Schema validation
json2ubl/
βββ converter.py # Main conversion API
βββ core/
β βββ mapper.py # JSON-to-schema mapping
β βββ validator.py # XML validation
β βββ serializer.py # JSON-to-XML serialization
β βββ schema_cache_builder.py # XSD-to-cache compilation
βββ schemas/
β βββ ubl-2.1/ # Official UBL 2.1 XSD files
β βββ cache/ # Pre-compiled schema caches
βββ models/ # Pydantic type hints (reference)
- Load Schema - Loads UBL 2.1 XSD schema for document type
- Normalize - Converts JSON keys to lowercase for case-insensitive matching
- Map - Matches JSON fields to schema fields automatically
- Validate - Checks required fields, types, and constraints
- Serialize - Builds XML tree with proper namespaces and structure
- Write - Outputs to file or returns XML string
Key Design:
- No hardcoded field mappings per document type
- Schema-driven β works for all UBL types automatically
- Efficient caching of parsed XSD structures
- Single document: ~50-100ms (depends on complexity)
- Batch (100 docs): ~5-10 seconds
- Memory: ~50MB for full schema cache
- CPU: Minimal (schema-driven, not iterative)
Benchmark results on production invoices with 20+ line items:
- Conversion: 2.5ms per invoice
- XML serialization: 1.2ms per invoice
- File I/O: 0.8ms per file
Contributions welcome! Please:
- Fork the repository
- Create a feature branch
- Add tests for new functionality
- Ensure all tests pass:
pytest tests/ -v - Submit a pull request
MIT License - see LICENSE file for details
- Built with lxml for XML processing
- Validation via Pydantic
- Logging via loguru
- UBL 2.1 specifications: OASIS
- Issues: GitHub Issues
- Discussions: GitHub Discussions
- Email: shaikh.sheroz07@gmail.com
Made with β€οΈ for data integration teams