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

eba

post_conversion

Rules applied

Typical use case

.xbrl

False

XML rules (EBA = No)

Basic XBRL structural check

.xbrl

True

All XML rules

Full EBA validation of an xBRL-XML file

.zip

False

False

CSV rules (EBA = No)

Standard xBRL-CSV structural check

.zip

True

False

All CSV rules

Full EBA validation of a standalone xBRL-CSV package

.zip

True

True

EBA semantic checks only

EBA compliance check after xbridge conversion

Severity Levels

Following the EBA Filing Rules language conventions (RFC 2119):

Severity

Meaning

ERROR

Violation of a MUST rule. The file is invalid.

WARNING

Violation of a SHOULD rule. The file is technically acceptable but deviates from best practice.

INFO

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

XML-*, EBA-*

.xbrl file

CSV Package Rules

CSV-*, EBA-*

.zip file

Naming Rules

EBA-NAME-*

Both formats (when eba=True)

Key points:

  • XML and CSV rules are mutually exclusive — they are selected by the file extension.

  • Within each set, rules with EBA = No always run. Rules with EBA = Yes only run when eba=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 a ValidationResult.

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 when eba=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 format

  • message (str): Human-readable description of the issue

  • location (str): File path, XPath, or row:column locator pointing to the problem

  • context (dict or null): 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 str or pathlib.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 is False.

param post_conversion:

When True and 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 .xbrl files. Default is False.

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

code

str

Unique rule identifier (e.g. "XML-001", "CSV-012").

message

str

Human-readable template. Supports {placeholder} syntax filled from the finding context dict at runtime.

severity

"ERROR" / "WARNING" / "INFO"

Default severity level.

xml

bool

true if the rule applies to .xbrl input.

csv

bool

true if the rule applies to .zip input.

eba

bool

true if the rule only runs when eba=True.

post_conversion

bool

true = rule survives post-conversion mode; false = skipped.

eba_ref

str / null

EBA Filing Rules v5.8 section reference.

csv_severity

(optional)

Overrides severity when running in CSV context.

csv_message

(optional)

Overrides message when running in CSV context.

Operational changes via the registry:

  • Adjust severity — Change the severity (or csv_severity) value.

  • Disable a rule — Remove the entry, or set both xml and csv to false.

  • Enable/disable EBA gating — Toggle the eba field.

  • Toggle post-conversion survival — Toggle the post_conversion field.

  • Edit the user-facing message — Modify the message (or csv_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.