Document Purpose: Explain how XBRL processors generate XSD schemas at design-time for efficient instance validation
Target Audience: XBRL processor developers, architects
Last Updated: January 2026
The Problem:
XBRL taxonomies are complex multi-file structures. Validating instance documents against these taxonomies is computationally expensive if done at runtime using full DTS traversal.
The Solution:
Generate a consolidated XSD schema at design-time (when loading taxonomy) that represents all valid instance structures. Use standard XSD validation at runtime (fast!).
Key Insight:
Runtime Validation Against Taxonomy (SLOW):
├─ Load DTS
├─ Traverse relationships
├─ Check dimensional constraints
├─ Validate formulas
└─ Time: Seconds to minutes
Runtime Validation Against Generated XSD (FAST):
├─ Load generated schema
├─ Standard XSD validation
└─ Time: Milliseconds
When Generation Happens:
At design-time, when entry point is loaded. NOT at runtime.
GLOMIDCO Implementation:
Generates XSD from presentation linkbase, incorporates instance schema requirements, handles dimensions through segment/scenario, includes custom laws (TaxonomyLaws.xml, ReportingLaws.xml).
Phase 1: Design-Time (Once)
Load Taxonomy Entry Point
↓
Traverse DTS (Explicit + Implicit)
↓
Identify All Valid Concepts
↓
Identify All Valid Structures
↓
GENERATE XSD Schema
↓
Store Generated Schema
Phase 2: Runtime (Many Times)
Receive Instance Document
↓
Load Generated XSD Schema (fast!)
↓
Standard XSD Validation
↓
Valid or Invalid (with errors)
Benefits:
Input: XBRL Taxonomy (DTS)
DTS Contains:
├─ Schemas (concept definitions)
├─ Label linkbase (labels)
├─ Presentation linkbase (structure)
├─ Calculation linkbase (calculations)
├─ Definition linkbase (dimensions)
└─ Reference linkbase (authoritative refs)
Output: Generated XSD Schema
Generated Schema Contains:
├─ All valid concepts as elements
├─ Hierarchical structure from presentation linkbase
├─ Type constraints from concept definitions
├─ Context and unit definitions from XBRL instance schema
├─ Segment/scenario structures for dimensions
└─ Custom constraints from laws
Key Point:
Generated schema represents what valid instances look like, NOT the taxonomy structure itself.
Critical Reference Document:
http://www.xbrl.org/2003/instance/xbrl-instance-2003-12-31.xsd
This schema defines:
<!-- Core XBRL instance structure -->
<xsd:complexType name="xbrl">
<xsd:sequence>
<xsd:element ref="link:schemaRef" minOccurs="0" maxOccurs="unbounded"/>
<xsd:element ref="link:linkbaseRef" minOccurs="0" maxOccurs="unbounded"/>
<xsd:element ref="xbrli:context" minOccurs="0" maxOccurs="unbounded"/>
<xsd:element ref="xbrli:unit" minOccurs="0" maxOccurs="unbounded"/>
<xsd:any namespace="##other" minOccurs="0" maxOccurs="unbounded"/>
</xsd:sequence>
</xsd:complexType>
<!-- Context structure -->
<xsd:complexType name="context">
<xsd:sequence>
<xsd:element ref="xbrli:entity"/>
<xsd:element ref="xbrli:period"/>
<xsd:element ref="xbrli:scenario" minOccurs="0"/>
</xsd:sequence>
<xsd:attribute name="id" type="xsd:ID" use="required"/>
</xsd:complexType>
<!-- Entity with segment (for dimensions) -->
<xsd:complexType name="entity">
<xsd:sequence>
<xsd:element ref="xbrli:identifier"/>
<xsd:element ref="xbrli:segment" minOccurs="0"/>
</xsd:sequence>
</xsd:complexType>
Generated Schema Must:
After Traversal (Explicit + Implicit):
DTS Object Contains:
├─ All concepts (from schemas)
├─ All relationships (from linkbases)
├─ Presentation hierarchy (presentation linkbase)
├─ Calculation relationships (calculation linkbase)
├─ Dimensional structures (definition linkbase)
└─ Complete taxonomy knowledge
Key Question:
Which concepts should be in generated schema?
Answer:
Pick root concepts from presentation linkbase and include their descendants.
Presentation Linkbase Provides Structure:
<!-- Presentation linkbase -->
<link:presentationLink role="http://example.com/role/BalanceSheet">
<!-- Root: Assets -->
<link:loc xlink:href="concepts.xsd#Assets" xlink:label="assets_loc"/>
<!-- Child: CurrentAssets -->
<link:loc xlink:href="concepts.xsd#CurrentAssets" xlink:label="current_loc"/>
<link:presentationArc xlink:from="assets_loc"
xlink:to="current_loc"
order="1.0"/>
<!-- Child: NonCurrentAssets -->
<link:loc xlink:href="concepts.xsd#NonCurrentAssets" xlink:label="noncurrent_loc"/>
<link:presentationArc xlink:from="assets_loc"
xlink:to="noncurrent_loc"
order="2.0"/>
</link:presentationLink>
Extract Hierarchy:
Root: Assets
├─ CurrentAssets (order 1.0)
│ ├─ Cash
│ └─ Receivables
└─ NonCurrentAssets (order 2.0)
├─ Property
└─ Equipment
Root Concepts:
High-Level Algorithm:
public XSDSchema generateInstanceSchema(DTS dts, EntryPoint entryPoint) {
// 1. Start with XBRL instance schema base
XSDSchema schema = loadBaseSchema(
"http://www.xbrl.org/2003/instance/xbrl-instance-2003-12-31.xsd"
);
// 2. Extract root concepts from presentation linkbase
List<Concept> rootConcepts = extractRootConcepts(
dts.getPresentationLinkbase()
);
// 3. For each root concept, generate element definition
for (Concept concept : rootConcepts) {
XSDElement element = generateElement(concept, dts);
schema.addElement(element);
// Recursively add child concepts
addChildElements(element, concept, dts);
}
// 4. Add context/unit definitions (from instance schema)
addContextDefinitions(schema);
addUnitDefinitions(schema);
// 5. Add dimensional structures (segment/scenario)
addDimensionalStructures(schema, dts);
// 6. Apply laws (TaxonomyLaws.xml, ReportingLaws.xml)
applyLaws(schema, dts);
// 7. Optimize and validate generated schema
optimizeSchema(schema);
validateGeneratedSchema(schema);
return schema;
}
Detailed Steps:
Step 1: Generate Element for Concept
private XSDElement generateElement(Concept concept, DTS dts) {
XSDElement element = new XSDElement();
// Name from concept
element.setName(concept.getQName());
// Type from concept
element.setType(concept.getType());
// Additional constraints
if (concept.isAbstract()) {
element.setAbstract(true);
}
if (concept.isNillable()) {
element.setNillable(true);
}
// Substitution group
element.setSubstitutionGroup(concept.getSubstitutionGroup());
// Period type (as annotation)
element.addAnnotation("periodType", concept.getPeriodType());
return element;
}
Step 2: Add Child Elements Recursively
private void addChildElements(XSDElement parent, Concept parentConcept, DTS dts) {
// Get children from presentation linkbase
List<Concept> children = dts.getPresentationChildren(parentConcept);
for (Concept child : children) {
XSDElement childElement = generateElement(child, dts);
parent.addChildElement(childElement);
// Recurse
addChildElements(childElement, child, dts);
}
}
Step 3: Add Context Definitions
private void addContextDefinitions(XSDSchema schema) {
// Based on xbrl-instance-2003-12-31.xsd
XSDComplexType contextType = new XSDComplexType("context");
// Entity
XSDElement entity = new XSDElement("entity", "entityType");
contextType.addElement(entity);
// Period
XSDElement period = new XSDElement("period", "periodType");
contextType.addElement(period);
// Scenario (optional, for dimensions)
XSDElement scenario = new XSDElement("scenario", "scenarioType");
scenario.setMinOccurs(0);
contextType.addElement(scenario);
// ID attribute
XSDAttribute id = new XSDAttribute("id", "xsd:ID");
id.setUse("required");
contextType.addAttribute(id);
schema.addComplexType(contextType);
}
Input Taxonomy:
Presentation Linkbase:
├─ Assets (root)
│ ├─ CurrentAssets
│ └─ NonCurrentAssets
└─ Liabilities (root)
├─ CurrentLiabilities
└─ NonCurrentLiabilities
Generated XSD (Simplified):
<?xml version="1.0" encoding="UTF-8"?>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xbrli="http://www.xbrl.org/2003/instance"
xmlns:company="http://example.com/company/2024"
targetNamespace="http://example.com/company/2024">
<!-- Import XBRL instance schema -->
<xsd:import namespace="http://www.xbrl.org/2003/instance"
schemaLocation="http://www.xbrl.org/2003/xbrl-instance-2003-12-31.xsd"/>
<!-- Root element: xbrl instance -->
<xsd:element name="xbrl" type="xbrli:xbrl"/>
<!-- Concept: Assets (root) -->
<xsd:element name="Assets"
type="xbrli:monetaryItemType"
substitutionGroup="xbrli:item"
xbrli:periodType="instant">
<xsd:annotation>
<xsd:documentation>Total assets</xsd:documentation>
</xsd:annotation>
</xsd:element>
<!-- Concept: CurrentAssets (child of Assets) -->
<xsd:element name="CurrentAssets"
type="xbrli:monetaryItemType"
substitutionGroup="xbrli:item"
xbrli:periodType="instant"/>
<!-- Concept: NonCurrentAssets (child of Assets) -->
<xsd:element name="NonCurrentAssets"
type="xbrli:monetaryItemType"
substitutionGroup="xbrli:item"
xbrli:periodType="instant"/>
<!-- Concept: Liabilities (root) -->
<xsd:element name="Liabilities"
type="xbrli:monetaryItemType"
substitutionGroup="xbrli:item"
xbrli:periodType="instant"/>
<!-- ... more concepts ... -->
<!-- Context structure with segment for dimensions -->
<xsd:complexType name="contextType">
<xsd:sequence>
<xsd:element name="entity">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="identifier" type="xsd:string"/>
<xsd:element name="segment" minOccurs="0">
<xsd:complexType>
<!-- Dimensional members here -->
<xsd:any namespace="##other"
processContents="lax"
minOccurs="0"
maxOccurs="unbounded"/>
</xsd:complexType>
</xsd:element>
</xsd:sequence>
</xsd:complexType>
</xsd:element>
<xsd:element name="period">
<!-- instant or duration -->
</xsd:element>
<xsd:element name="scenario" minOccurs="0">
<!-- Preferred location for dimensions -->
</xsd:element>
</xsd:sequence>
<xsd:attribute name="id" type="xsd:ID" use="required"/>
</xsd:complexType>
</xsd:schema>
Problem:
Dimensions are defined in definition linkbase but manifest in instance documents as segment or scenario content.
Definition Linkbase (Taxonomy):
<!-- Dimension: BusinessSegmentAxis -->
<xsd:element name="BusinessSegmentAxis"
type="xbrli:dimensionItemType"
substitutionGroup="xbrldt:dimensionItem"/>
<!-- Members -->
<xsd:element name="RetailSegmentMember"
substitutionGroup="xbrldt:domainItem"/>
<xsd:element name="WholesaleSegmentMember"
substitutionGroup="xbrldt:domainItem"/>
Instance Document:
<context id="c1">
<entity>
<identifier>0001234567</identifier>
<segment>
<!-- THIS IS WHERE DIMENSIONS GO -->
<xbrldi:explicitMember dimension="company:BusinessSegmentAxis">
company:RetailSegmentMember
</xbrldi:explicitMember>
</segment>
</entity>
<period><instant>2024-12-31</instant></period>
</context>
Challenge for Schema Generation:
How to represent dimensional constraints in generated XSD?
XBRL Specification:
Dimensions can appear in either segment or scenario.
GLOMIDCO Preference: Scenario
Reasoning:
Segment:
├─ Original purpose: Entity-specific information
├─ Less commonly used for dimensions
└─ Can conflict with other segment content
Scenario:
├─ Purpose: Distinguish different reporting scenarios
├─ Natural fit for dimensional reporting
├─ Preferred in modern XBRL practice
└─ GLOMIDCO default
Configuration:
In TaxonomyLaws.xml or ReportingLaws.xml:
<law name="DimensionLocation">
<value>scenario</value> <!-- or "segment" -->
</law>
Approach: Open Content Model
<!-- Scenario element allows any dimensional content -->
<xsd:element name="scenario" minOccurs="0">
<xsd:complexType>
<xsd:sequence>
<!-- Allow any dimensional member -->
<xsd:any namespace="##other"
processContents="lax"
minOccurs="0"
maxOccurs="unbounded"/>
</xsd:sequence>
</xsd:complexType>
</xsd:element>
Why Open Content:
Note in GLOMIDCO:
"Je kunt de dimensions options niet in het schema gebruiken"
(You cannot use dimension options in the schema)
Translation: Dimensional validation happens in DTS validation phase, not XSD validation phase.
Two Types of Laws:
1. Taxonomy Laws (TaxonomyLaws.xml)
<!-- In taxonomy package: META-INF/TaxonomyLaws.xml -->
<taxonomyLaws>
<law name="AllowExtensions">
<value>true</value>
</law>
<law name="RequireCalculationLinkbase">
<value>true</value>
</law>
<law name="MaximumDecimalPlaces">
<value>4</value>
</law>
<law name="DimensionLocation">
<value>scenario</value> <!-- segment or scenario -->
</law>
</taxonomyLaws>
2. Reporting Laws (ReportingLaws.xml)
<!-- In taxonomy package: META-INF/ReportingLaws.xml -->
<reportingLaws>
<law name="MandatorySignature">
<value>true</value>
</law>
<law name="MaximumFileSize">
<value>10485760</value> <!-- 10 MB -->
</law>
<law name="RequiredContexts">
<value>current,prior</value>
</law>
</reportingLaws>
During Schema Generation:
private void applyLaws(XSDSchema schema, DTS dts) {
TaxonomyLaws taxLaws = dts.getTaxonomyLaws();
ReportingLaws repLaws = dts.getReportingLaws();
// Apply taxonomy laws
for (Law law : taxLaws.getLaws()) {
if (law.getName().equals("DimensionLocation")) {
String location = law.getValue();
if (location.equals("scenario")) {
schema.setDimensionLocation(DimensionLocation.SCENARIO);
} else if (location.equals("segment")) {
schema.setDimensionLocation(DimensionLocation.SEGMENT);
}
}
if (law.getName().equals("MaximumDecimalPlaces")) {
int maxDecimals = Integer.parseInt(law.getValue());
schema.setMaximumDecimals(maxDecimals);
}
// ... more laws
}
// Apply reporting laws
for (Law law : repLaws.getLaws()) {
// ... process reporting laws
}
}
Note from GLOMIDCO:
tag: value = " "
betekent dat er niets mee wordt gedaan
Translation: <value> </value> (empty/whitespace) means "do nothing with this law"
Implementation:
private void applyLaw(Law law, XSDSchema schema) {
String value = law.getValue().trim();
if (value.isEmpty()) {
// Empty value: law is disabled, do nothing
log("Law " + law.getName() + " disabled (empty value)");
return;
}
// Process law...
}
Note from GLOMIDCO:
overwrite default met TRUE of FALSE
Translation: Override defaults with TRUE or FALSE
Example:
<!-- Default: Extensions not allowed -->
<law name="AllowExtensions">
<value>false</value> <!-- Default -->
</law>
<!-- Override in entry point -->
<law name="AllowExtensions">
<value>true</value> <!-- Override to allow -->
<override>true</override>
</law>
Implementation:
private void mergeLaws(TaxonomyLaws defaults, TaxonomyLaws overrides) {
for (Law override : overrides.getLaws()) {
if (override.isOverride()) {
defaults.setLaw(override.getName(), override.getValue());
log("Overriding law: " + override.getName() + " = " + override.getValue());
}
}
}
Note from GLOMIDCO:
FRTA dicteert de NL-Fris en Fris rules
XBRL nederlandse NTA is niet apart opgenomen
Translation:
What This Means:
FRTA (Financial Reporting Taxonomy Architecture):
NL-FRIS (Netherlands Financial Reporting Instance Standard):
FRIS (Financial Reporting Instance Standard):
Implementation:
These rules are incorporated into TaxonomyLaws.xml and ReportingLaws.xml, so they don't need separate processing.
Note from GLOMIDCO:
In tax (zip) en in package: in META-INF directory (hier mag je zetten wat je wilt)
Translation: In taxonomy (ZIP) and package: in META-INF directory (you can put anything here)
Taxonomy Package Structure:
company-taxonomy-2024.zip
├─ META-INF/
│ ├─ catalog.xml (OASIS XML Catalog)
│ ├─ taxonomyPackage.xml (XBRL standard)
│ ├─ TaxonomyLaws.xml (GLOMIDCO standard)
│ └─ ReportingLaws.xml (GLOMIDCO standard)
├─ schemas/
│ ├─ company-concepts.xsd
│ └─ company-types.xsd
├─ linkbases/
│ ├─ company-labels-en.xml
│ ├─ company-labels-nl.xml
│ ├─ company-presentation.xml
│ ├─ company-calculation.xml
│ └─ company-definition.xml
└─ entrypoints/
├─ annual-report.xsd
└─ vat-return.xsd
Purpose: URI Redirections
<?xml version="1.0" encoding="UTF-8"?>
<catalog xmlns="urn:oasis:names:tc:entity:xmlns:xml:catalog">
<!-- Redirect XBRL.org schemas to local copies -->
<uri name="http://www.xbrl.org/2003/instance/xbrl-instance-2003-12-31.xsd"
uri="../cache/xbrl-instance-2003-12-31.xsd"/>
<uri name="http://www.xbrl.org/2003/linkbase/xbrl-linkbase-2003-12-31.xsd"
uri="../cache/xbrl-linkbase-2003-12-31.xsd"/>
<!-- Redirect company schemas -->
<uri name="http://example.com/company/2024/concepts.xsd"
uri="schemas/company-concepts.xsd"/>
</catalog>
Benefits:
Purpose: Package Metadata and Entry Points
<?xml version="1.0" encoding="UTF-8"?>
<taxonomyPackage
xmlns="http://xbrl.org/2016/taxonomy-package">
<identifier>http://example.com/company/2024/package</identifier>
<version>1.0</version>
<publisher>Example Company</publisher>
<publisherURL>http://example.com</publisherURL>
<publisherCountry>NL</publisherCountry>
<publicationDate>2024-01-01</publicationDate>
<!-- Entry points -->
<entryPoints>
<entryPoint>
<name>Annual Report</name>
<description>Company annual financial report</description>
<entryPointDocument>entrypoints/annual-report.xsd</entryPointDocument>
</entryPoint>
<entryPoint>
<name>VAT Return</name>
<description>Quarterly VAT return</description>
<entryPointDocument>entrypoints/vat-return.xsd</entryPointDocument>
</entryPoint>
</entryPoints>
</taxonomyPackage>
Purpose: Taxonomy-Level Constraints
<?xml version="1.0" encoding="UTF-8"?>
<taxonomyLaws xmlns="http://glomidco.nl/taxonomylaws">
<law name="AllowExtensions">
<value>true</value>
<description>Allow company extensions to taxonomy</description>
</law>
<law name="DimensionLocation">
<value>scenario</value>
<description>Place dimensions in scenario (not segment)</description>
</law>
<law name="RequireCalculationLinkbase">
<value>true</value>
<description>Taxonomy must include calculation linkbase</description>
</law>
<law name="MaximumConceptDepth">
<value>10</value>
<description>Maximum presentation hierarchy depth</description>
</law>
<law name="ValidateFormulaConsistency">
<value>true</value>
<description>Check formula consistency at load time</description>
</law>
</taxonomyLaws>
Purpose: Instance-Level Constraints
<?xml version="1.0" encoding="UTF-8"?>
<reportingLaws xmlns="http://glomidco.nl/reportinglaws">
<law name="RequiredContexts">
<value>current,prior</value>
<description>Must include current and prior period contexts</description>
</law>
<law name="MaximumFileSize">
<value>10485760</value> <!-- 10 MB -->
<description>Maximum instance file size in bytes</description>
</law>
<law name="MandatorySignature">
<value>true</value>
<description>Instance must be digitally signed</description>
</law>
<law name="AllowedDecimalPlaces">
<value>0,2,4</value>
<description>Allowed decimal places for monetary values</description>
</law>
<law name="RequirePriorPeriodComparison">
<value>true</value>
<description>Must include prior period comparative data</description>
</law>
</reportingLaws>
Stage 1: XSD Validation (Fast)
public ValidationResult validateInstance(File instanceFile, XSDSchema generatedSchema) {
// Standard XSD validation
SchemaFactory factory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
Schema schema = factory.newSchema(generatedSchema.getFile());
Validator validator = schema.newValidator();
try {
validator.validate(new StreamSource(instanceFile));
log("XSD validation: PASSED");
} catch (SAXException e) {
log("XSD validation: FAILED - " + e.getMessage());
return ValidationResult.failed(e.getMessage());
}
return ValidationResult.passed();
}
Stage 2: XBRL Validation (Comprehensive)
public ValidationResult validateXBRL(File instanceFile, DTS dts) {
// Load instance
XBRLInstance instance = loadInstance(instanceFile);
// Validate calculations
CalculationValidator calcValidator = new CalculationValidator(dts);
ValidationResult calcResult = calcValidator.validate(instance);
// Validate formulas
FormulaValidator formulaValidator = new FormulaValidator(dts);
ValidationResult formulaResult = formulaValidator.validate(instance);
// Validate dimensions
DimensionValidator dimValidator = new DimensionValidator(dts);
ValidationResult dimResult = dimValidator.validate(instance);
// Apply reporting laws
LawValidator lawValidator = new LawValidator(dts.getReportingLaws());
ValidationResult lawResult = lawValidator.validate(instance);
// Combine results
return ValidationResult.combine(calcResult, formulaResult, dimResult, lawResult);
}
Without Generated Schema:
Validate Instance:
1. Load taxonomy DTS (2-5 seconds)
2. Traverse all linkbases (1-2 seconds)
3. Build validation structures (1-2 seconds)
4. Validate instance (1-2 seconds)
Total: 5-11 seconds per instance
For 1,000 instances: 1.4-3 hours!
With Generated Schema:
Validate Instance:
1. Load generated XSD (0.1 seconds, cached)
2. XSD validation (0.05-0.2 seconds)
3. XBRL-specific validation (0.5-1 seconds)
Total: 0.65-1.3 seconds per instance
For 1,000 instances: 11-22 minutes!
Speedup: 5-10x faster!
Note from GLOMIDCO:
TODO Tables in schema is nog niet verder uitgewerkt
Translation: TODO Tables in schema not yet worked out
Challenge:
Table Linkbase defines complex 3D structures (X/Y/Z axes) that are difficult to represent in XSD.
Current Approach:
Why Tables Are Hard:
Table Linkbase:
├─ Dynamic structures (open axes)
├─ Breakdown trees (recursive)
├─ Aspect rules (complex constraints)
└─ Cannot be expressed in XSD easily
XSD Limitation:
├─ Static structure only
├─ No dynamic element creation
├─ No complex conditional constraints
└─ Not suitable for table validation
Possible Solutions:
Option 1: Schematron Rules
<!-- Use Schematron for table validation -->
<sch:pattern>
<sch:rule context="xbrli:xbrl">
<sch:assert test="count(fact[@table='C_01.00']) >= 10">
Table C_01.00 must have at least 10 rows
</sch:assert>
</sch:rule>
</sch:pattern>
Option 2: Separate Table Schema
Generate separate schema for each table
Include in generated schema as optional
Validate table-specific instances separately
Option 3: Runtime Only
Accept that tables cannot be in XSD
Keep table validation in XBRL processor only
Focus generated schema on concept validation
GLOMIDCO Current: Option 3
DO:
DON'T:
DO:
DON'T:
DO:
DON'T:
Design-Time XSD Generation:
Key Components:
When It Happens:
Design-time, when loading taxonomy entry point
Performance Benefit:
5-10x faster instance validation
GLOMIDCO Innovation:
Limitations:
Best Practice:
Use generated XSD for structural validation, XBRL processor for semantic validation.
This document explains the sophisticated but poorly documented practice of generating XSD schemas at design-time for efficient XBRL instance validation.