Skip to main content

json2ubl

Production-grade JSON to UBL 2.1 XML converter with schema-driven mapping

PyPI version Python Versions License: MIT Code style: black

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.


✨ Features

  • 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

📦 Installation

pip install json2ubl

Requirements:

  • Python >= 3.10
  • lxml >= 4.9.4
  • pydantic >= 2.7.0
  • pyyaml >= 6.0.1
  • loguru >= 0.7.2

🚀 Quickstart

Convert Multiple Documents (List)

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 string

Convert JSON File to XML Dicts

from 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"])

Write to XML Files

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")

📊 Document Types

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


🔧 API Reference

json_dict_to_ubl_xml(list_of_dicts: List[Dict]) -> Dict

Convert list of JSON dicts to UBL 2.1 XML strings in memory.

Args:

  • list_of_dicts: List of document dicts with document_type (numeric code) and schema fields
  • config_path: Optional path to ubl_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}
    }
}

json_file_to_ubl_xml_dict(json_file_path: str) -> Dict

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()

json_file_to_ubl_xml_files(json_file_path: str, output_dir: str) -> Dict

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


🛡️ Error Handling

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_type field → Error with guidance
  • Invalid document_type code → Lists valid codes
  • Null input fields → Preserved as empty XML elements
  • Multi-page documents → Automatically merged (with configurable strategy)

🧪 Testing

Run tests:

pip install -e .[dev]
pytest tests/ -v

Test coverage includes:

  • All 60+ UBL document types
  • Multi-page document merging
  • Error handling and rollback
  • Concurrent batch processing
  • Schema validation

🏗️ Architecture

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)

🔍 How It Works

  1. Load Schema - Loads UBL 2.1 XSD schema for document type
  2. Normalize - Converts JSON keys to lowercase for case-insensitive matching
  3. Map - Matches JSON fields to schema fields automatically
  4. Validate - Checks required fields, types, and constraints
  5. Serialize - Builds XML tree with proper namespaces and structure
  6. 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

📈 Performance

  • 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

🤝 Contributing

Contributions welcome! Please:

  1. Fork the repository
  2. Create a feature branch
  3. Add tests for new functionality
  4. Ensure all tests pass: pytest tests/ -v
  5. Submit a pull request

📄 License

MIT License - see LICENSE file for details


🙏 Acknowledgments


📞 Support


Made with ❤️ for data integration teams

Metadata

Release files for json2ubl 1.0.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for json2ubl 1.0.1
File Size Uploaded
json2ubl-1.0.1.tar.gz 6.5 MB Details

Built distribution (wheel)

Table of built distributions (wheels) for json2ubl 1.0.1
File Interpreter ABI Platform
json2ubl-1.0.1-py3-none-any.whl Python 3 none any Details

Total release size: 13.9 MB

Release files / json2ubl-1.0.1.tar.gz

Download URL json2ubl-1.0.1.tar.gz
Size 6.5 MB
Tags Source
SHA-256 checksum
How to use checksums
3e05aeb40b72c88171b549d398a128ea4ce0376adf75f1606d036ab40bb0daf0
BLAKE2b-256 checksum
How to use checksums
c1022fefb06a4e2ca260bb7b8fb02cb9d3226d655f607028b36c445783cad724
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.11.13

Release files / json2ubl-1.0.1-py3-none-any.whl

Download URL json2ubl-1.0.1-py3-none-any.whl
Size 7.4 MB
Tags Python 3
SHA-256 checksum
How to use checksums
ca83b2a08174e8707cdb0dc632452c830fb09ee69a68e3db13a33e94888370ef
BLAKE2b-256 checksum
How to use checksums
82ec921969538d3801bbb4bb97582e6b3225d7e9e5e024e4ec1180d527a6c037
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.11.13

Release history Release notifications | RSS feed

This release

1.0.1 This release

2 release files

1.0.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page