Validation API
The xbridge.validation module provides a standalone validation API for checking XBRL instance files against structural and regulatory rules.
Overview
The validation module can check both XBRL-XML (.xbrl) and XBRL-CSV (.zip) files. It runs a configurable set of validation rules and returns findings grouped by severity.
Key features:
Format-aware: Automatically detects whether the input is XML or CSV and applies the appropriate rules
EBA rules: Optionally enables EBA-specific regulatory rules (entity, currency, decimals, etc.)
Post-conversion mode: For CSV files generated by xbridge, skips structural checks guaranteed by the converter
Extensible registry: Rules are declared in a JSON registry and can be customised without changing code
Taxonomy-less: All taxonomy-derived metadata is pre-processed and shipped as JSON — no network access, no taxonomy resolution, no XML schema parsing at validation time
See also
- Validation Rules
Complete catalogue of all validation rules with their attributes.
Design
Orthogonal Parameters
Input format (XML / CSV) and EBA filing rules (on / off) are independent choices. Any combination is valid:
File |
|
|
Rules applied |
Typical use case |
|---|---|---|---|---|
|
|
– |
XML rules (EBA = No) |
Basic XBRL structural check |
|
|
– |
All XML rules |
Full EBA validation of an xBRL-XML file |
|
|
|
CSV rules (EBA = No) |
Standard xBRL-CSV structural check |
|
|
|
All CSV rules |
Full EBA validation of a standalone xBRL-CSV package |
|
|
|
EBA semantic checks only |
EBA compliance check after xbridge conversion |
Severity Levels
Following the EBA Filing Rules language conventions (RFC 2119):
Severity |
Meaning |
|---|---|
|
Violation of a MUST rule. The file is invalid. |
|
Violation of a SHOULD rule. The file is technically acceptable but deviates from best practice. |
|
Informational observation. No rule is violated. |
Rule Organization
Rules are organized into two sets based on the input format. Each rule carries attributes that control when it is executed:
Rule set |
Prefixes |
Applies when |
|---|---|---|
XML Instance Rules |
|
|
CSV Package Rules |
|
|
Naming Rules |
|
Both formats (when |
Key points:
XML and CSV rules are mutually exclusive — they are selected by the file extension.
Within each set, rules with
EBA = Noalways run. Rules withEBA = Yesonly run wheneba=True.EBA-*rules that are format-independent (entity identification, decimals, currency, etc.) appear in both rule sets with format-appropriate implementations.
Post-conversion Mode
When xbridge converts an XML file to CSV, it first validates the XML input and then generates the CSV output. A subsequent validation of the CSV output should not repeat checks that are guaranteed by construction:
Structural and format checks: xbridge generates correct package structure, metadata, parameters, filing indicators, and data tables. These are redundant.
Taxonomy conformance: Already validated on the XML source.
Logical checks already applied to XML: Entity identification, period validity, decimals validity, filing indicator codes — already enforced on the XML input.
When post_conversion=True, all CSV rules with Post-conv. = No are
skipped. Only EBA semantic checks explicitly marked Post-conv. = Yes survive
— specifically the unit, representation, additional, and guidance checks.
Quick Start
from xbridge.validation import validate
# Validate an XBRL-XML file
results = validate("path/to/instance.xbrl")
has_errors = any(section["errors"] for section in results.values())
if not has_errors:
print("No issues found.")
else:
for scope, section in results.items():
for code, findings in section["errors"].items():
for f in findings:
print(f"[{scope}] [{f['severity']}] {f['rule_id']}: {f['message']}")
Public API
validate()
- xbridge.validation.validate(file: str | Path, eba: bool = False, post_conversion: bool = False) Dict[str, Dict[str, Dict[str, List[Dict[str, Any]]]]]
Validate an XBRL instance file.
- Parameters:
file – Path to an .xbrl (XML) or .zip (CSV) file.
eba – When True, additionally runs EBA-specific rules.
post_conversion – (CSV only) When True, skips structural and format checks guaranteed by xbridge’s converter, keeping only EBA semantic checks. Has no effect for .xbrl files.
- Returns:
"XBRL"— always present; results from XBRL-standard rules."EBA"— only present when eba is True; results from EBA-specific rules.
Each section contains:
"errors"— dict keyed by rule code, each value a list of ERROR findings as dicts."warnings"— dict keyed by rule code, each value a list of WARNING (and INFO) findings as dicts.
Each dict entry is the
to_dict()representation of aValidationResult.- Return type:
A dictionary keyed by validation scope
Usage Examples
Basic Validation
Check an XBRL-XML instance for structural issues:
from xbridge.validation import validate
results = validate("data/instance.xbrl")
for scope, section in results.items():
for code, findings in section["errors"].items():
for f in findings:
print(f"[{scope}] [{f['severity']}] {f['rule_id']}: {f['message']}")
print(f" Location: {f['location']}")
Enable EBA Rules
Run additional EBA-specific checks (entity format, decimal precision, currency, etc.):
from xbridge.validation import validate
results = validate("data/instance.xbrl", eba=True)
error_count = sum(
len(v) for section in results.values() for v in section["errors"].values()
)
warning_count = sum(
len(v) for section in results.values() for v in section["warnings"].values()
)
print(f"Errors: {error_count}, Warnings: {warning_count}")
Validate a CSV Package
Validate an XBRL-CSV ZIP package:
from xbridge.validation import validate
results = validate("data/report.zip", eba=True)
Post-Conversion Validation
When validating CSV output produced by xbridge’s own converter, use post_conversion=True to skip structural checks that the converter guarantees:
from xbridge.validation import validate
# Only run semantic/EBA checks, skip structural CSV checks
results = validate("data/converted_output.zip", eba=True, post_conversion=True)
Inspect Findings
The validate() function returns a dictionary keyed by validation scope:
"XBRL"— always present; results from XBRL-standard rules."EBA"— only present wheneba=True; results from EBA-specific rules.
Each scope contains:
"errors"— a dict keyed by rule code, each value a list of ERROR findings (as dicts)"warnings"— a dict keyed by rule code, each value a list of WARNING/INFO findings (as dicts)
Each finding dict has the following keys:
rule_id(str): The unique rule code (e.g."XML-001","EBA-ENTITY-001")severity(str):"ERROR","WARNING", or"INFO"rule_set(str):"xml"or"csv", indicating the input formatmessage(str): Human-readable description of the issuelocation(str): File path, XPath, or row:column locator pointing to the problemcontext(dictornull): Optional key-value data with diagnostic details
from xbridge.validation import validate
results = validate("data/instance.xbrl", eba=True)
for scope, section in results.items():
print(f"--- {scope} ---")
for code, findings in section["errors"].items():
for f in findings:
print(f"Rule: {f['rule_id']}")
print(f"Severity: {f['severity']}")
print(f"Message: {f['message']}")
print(f"Location: {f['location']}")
print(f"Context: {f['context']}")
print()
# Count errors across all scopes
error_count = sum(
len(v) for section in results.values() for v in section["errors"].values()
)
JSON Output Example
The return value is directly JSON-serialisable:
import json
from xbridge.validation import validate
results = validate("data/instance.xbrl", eba=True)
print(json.dumps(results, indent=2))
Sample output:
{
"XBRL": {
"errors": {
"XML-001": [
{
"rule_id": "XML-001",
"severity": "ERROR",
"rule_set": "xml",
"message": "File is not well-formed XML.",
"location": "instance.xbrl",
"context": null
}
]
},
"warnings": {}
}
}
Parameters
- param file:
Path to the XBRL instance file. Accepts
strorpathlib.Path. Supported extensions:.xbrl,.xml(XML format),.zip(CSV format).- param eba:
When
True, additionally runs EBA-specific validation rules (entity format, decimal precision, currency checks, etc.). Default isFalse.- param post_conversion:
When
Trueand validating a CSV (.zip) file, skips structural and format checks that are guaranteed by xbridge’s converter. Only EBA semantic checks are retained. Has no effect for.xbrlfiles. Default isFalse.- return:
A dictionary keyed by validation scope (
"XBRL"always present,"EBA"when eba is True). Each scope contains"errors"and"warnings"dicts keyed by rule code with lists of finding dicts.- raises FileNotFoundError:
If the file does not exist.
- raises ValueError:
If the file extension is not supported.
Integration with Conversion
You can validate before converting to catch issues early:
from xbridge.validation import validate
from xbridge.api import convert_instance
# Validate first
results = validate("data/instance.xbrl", eba=True)
has_errors = any(section["errors"] for section in results.values())
if has_errors:
print("Validation errors found — skipping conversion:")
for section in results.values():
for code, findings in section["errors"].items():
for e in findings:
print(f" [{e['rule_id']}] {e['message']}")
else:
convert_instance(
instance_path="data/instance.xbrl",
output_path="data/output"
)
print("Conversion complete.")
Architecture
This section describes the internal architecture of the validation module for developers who want to understand or extend it.
Rule Registry
All validation rules are declared in a single, editable JSON file:
src/xbridge/validation/registry.json.
The registry is the single source of truth for rule metadata. Behavioural changes — adjusting severity, toggling the EBA flag, changing messages, or disabling rules — are made by editing this file without touching implementation code.
Each entry in the registry has the following schema:
{
"code": "EBA-DEC-001",
"message": "Monetary facts: decimals MUST be >= {min_decimals}.",
"severity": "ERROR",
"xml": true,
"csv": true,
"eba": true,
"post_conversion": false,
"eba_ref": "2.18"
}
Field |
Type |
Description |
|---|---|---|
|
|
Unique rule identifier (e.g. |
|
|
Human-readable template. Supports |
|
|
Default severity level. |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
EBA Filing Rules v5.8 section reference. |
|
(optional) |
Overrides |
|
(optional) |
Overrides |
Operational changes via the registry:
Adjust severity — Change the
severity(orcsv_severity) value.Disable a rule — Remove the entry, or set both
xmlandcsvtofalse.Enable/disable EBA gating — Toggle the
ebafield.Toggle post-conversion survival — Toggle the
post_conversionfield.Edit the user-facing message — Modify the
message(orcsv_message) template.Add a new rule — Add an entry; if no implementation exists yet, the rule is silently skipped.
Module Structure
src/xbridge/validation/
├── __init__.py # Public API: validate(), ValidationResult, Severity
├── _models.py # RuleDefinition, ValidationResult, Severity
├── _registry.py # rule_impl decorator, get_rule_impl(), load_registry()
├── _engine.py # Rule selection logic, execution loop
├── _context.py # ValidationContext passed to rule functions
├── registry.json # The editable rule registry
└── rules/ # Rule implementations (one file per specification section)
├── __init__.py
├── xml_wellformedness.py # XML-001, XML-002, XML-003
├── xml_schema_ref.py # XML-010, XML-012
├── xml_filing_indicators.py
├── xml_context.py # XML-030 – XML-035
├── xml_facts.py # XML-040 – XML-043
├── xml_units.py # XML-050
├── xml_document.py # XML-060 – XML-069
├── xml_taxonomy.py # XML-070 – XML-072
├── csv_package.py # CSV-001 – CSV-006
├── csv_metadata.py # CSV-010 – CSV-016
├── csv_parameters.py # CSV-020 – CSV-026
├── csv_filing_indicators.py
├── csv_data_tables.py # CSV-040 – CSV-049
├── csv_facts.py # CSV-050 – CSV-052
├── csv_taxonomy.py # CSV-060 – CSV-062
├── eba_entity.py # EBA-ENTITY-001/002 (shared)
├── eba_decimals.py # EBA-DEC-001 – EBA-DEC-004 (shared)
├── eba_currency.py # EBA-CUR-001 – EBA-CUR-003 (shared)
├── eba_units.py # EBA-UNIT-001/002 (shared)
├── eba_additional.py # EBA-2.5, EBA-2.16.1, EBA-2.24, EBA-2.25
└── eba_guidance.py # EBA-GUIDE-001 – EBA-GUIDE-007
Rule Selection Logic
The engine selects which rules to execute based on the detected format and the caller’s parameters:
For each rule in registry:
if rule_set == "xml" and not rule.xml → SKIP
if rule_set == "csv" and not rule.csv → SKIP
if rule.eba and not eba_param → SKIP
if rule_set == "csv" and post_conversion
and not rule.post_conversion → SKIP
Otherwise → RUN
Rules without a registered implementation are silently skipped, supporting incremental development.
Rule Implementation Pattern
Each rule is implemented as a function decorated with @rule_impl:
from xbridge.validation._registry import rule_impl
from xbridge.validation._context import ValidationContext
# Format-specific rule — no format qualifier needed
@rule_impl("XML-001")
def check_wellformedness(ctx: ValidationContext) -> None:
...
# Shared rule — two implementations, one per format
@rule_impl("EBA-ENTITY-001", format="xml")
def check_entity_xml(ctx: ValidationContext) -> None:
...
@rule_impl("EBA-ENTITY-001", format="csv")
def check_entity_csv(ctx: ValidationContext) -> None:
...
The ValidationContext carries all data a rule implementation needs (parsed
instance, taxonomy module, file path) and provides add_finding() to report
findings. The message template from the registry is automatically rendered with
placeholders filled from the finding context dict.
Reference Documents
Document |
Version |
|---|---|
XBRL 2.1 Specification |
REC 2003-12-31, errata 2013-02-20 |
XBRL Dimensions 1.0 |
1.0 |
xBRL-CSV 1.0 |
REC 2021-10-13, errata 2023-04-19 |
EBA Filing Rules |
v5.8, February 2026 |
Note
XBRL Formula validation (EBA Filing Rules 1.10) is out of scope — it requires a full formula processor.