Document Purpose: Complete guide to Taxonomy Package and Report Package specifications
Last Updated: January 2026
Target Audience: XBRL processor developers implementing package support
Taxonomy Packages provide a standardized ZIP-based format for distributing XBRL taxonomies with metadata, entry points, and URL remapping for offline use.
Report Packages extend this concept to bundle complete XBRL reports with their taxonomies and supporting files into a single container.
Evolution:
A Taxonomy Package is:
Problems Solved:
Without Packages:
With Packages:
GLOMIDCO using a slighty different setup than shown below.
TaxonomyPackageName-v1.0.zip
└── TaxonomyPackageName-v1.0/ (top-level directory)
├── META-INF/ (required)
│ ├── taxonomyPackage.xml (required)
│ └── catalog.xml (optional)
├── taxonomy/ (taxonomy files)
│ ├── core/
│ │ ├── concepts.xsd
│ │ ├── labels.xml
│ │ ├── presentation.xml
│ │ ├── calculation.xml
│ │ └── definition.xml
│ └── extensions/
│ ├── industry-concepts.xsd
│ └── industry-labels.xml
└── documentation/ (optional)
└── readme.txt
Single Top-Level Directory
META-INF Directory (required)
taxonomyPackage.xmlcatalog.xmlTaxonomy Files
<?xml version="1.0" encoding="UTF-8"?>
<tp:taxonomyPackage
xmlns:tp="http://xbrl.org/2016/taxonomy-package"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xbrl.org/2016/taxonomy-package
http://xbrl.org/2016/taxonomy-package.xsd"
xml:lang="en">
<!-- Package Metadata -->
<tp:identifier>http://example.com/taxonomy/2024</tp:identifier>
<tp:name>Example GAAP Taxonomy 2024</tp:name>
<tp:name xml:lang="fr">Taxonomie GAAP Exemple 2024</tp:name>
<tp:description>
Example GAAP taxonomy for financial reporting,
including concepts for balance sheet, income statement,
and cash flow reporting.
</tp:description>
<tp:version>2024.1.0</tp:version>
<tp:publisher>Example Standards Board</tp:publisher>
<tp:publisherURL>http://example.com</tp:publisherURL>
<tp:publisherCountry>US</tp:publisherCountry>
<tp:publicationDate>2024-01-15</tp:publicationDate>
<!-- Supersession Information -->
<tp:supersededTaxonomyPackages>
<tp:taxonomyPackageRef>
http://example.com/taxonomy/2023
</tp:taxonomyPackageRef>
</tp:supersededTaxonomyPackages>
<!-- Versioning Reports -->
<tp:versioningReports>
<tp:versioningReport
href="http://example.com/taxonomy/versioning/2023-to-2024.xml"/>
</tp:versioningReports>
<!-- Entry Points -->
<tp:entryPoints>
<!-- Primary Entry Point -->
<tp:entryPoint>
<tp:name>Full Taxonomy</tp:name>
<tp:description>
Complete entry point including all concepts and linkbases
</tp:description>
<tp:version>2024.1.0</tp:version>
<tp:entryPointDocument
href="http://example.com/taxonomy/2024/entry-full.xsd"/>
</tp:entryPoint>
<!-- Core Only Entry Point -->
<tp:entryPoint>
<tp:name>Core Concepts Only</tp:name>
<tp:description>
Core concepts without industry extensions
</tp:description>
<tp:version>2024.1.0</tp:version>
<tp:entryPointDocument
href="http://example.com/taxonomy/2024/entry-core.xsd"/>
</tp:entryPoint>
<!-- Industry Extension Entry Point -->
<tp:entryPoint>
<tp:name>Manufacturing Extension</tp:name>
<tp:description>
Core concepts plus manufacturing industry extensions
</tp:description>
<tp:version>2024.1.0</tp:version>
<tp:entryPointDocument
href="http://example.com/taxonomy/2024/entry-manufacturing.xsd"/>
</tp:entryPoint>
</tp:entryPoints>
</tp:taxonomyPackage>
<tp:identifier> (required)
http://example.com/taxonomy/2024<tp:version> (required)
2024.1.0, 1.0, 2024-Q1<tp:publisher> (optional)
Financial Accounting Standards Board<tp:publisherURL> (optional)
http://www.fasb.org<tp:publisherCountry> (optional)
US, GB, FR<tp:publicationDate> (optional)
2024-01-15<tp:name> (required, multi-lingual)
xml:lang<tp:name>US GAAP Taxonomy 2024</tp:name>
<tp:name xml:lang="es">Taxonomía US GAAP 2024</tp:name>
<tp:description> (optional, multi-lingual)
xml:lang<tp:supersededTaxonomyPackages> (optional)
<tp:taxonomyPackageRef>
<tp:versioningReports> (optional)
<tp:versioningReport>
href attribute: URL to versioning report<tp:entryPoints> (optional but strongly recommended)
<tp:entryPoint> (one or more)
<tp:name> - Entry point name (multi-lingual)<tp:description> - Entry point description (multi-lingual)<tp:version> - Entry point version<tp:entryPointDocument> - Schema/linkbase file(s)<tp:entryPointDocument>
href attribute: URL to schema or linkbase fileEntry Point Order:
Imagine receiving a taxonomy ZIP file:
US-GAAP-2024.zip
├── schemas/
│ ├── us-gaap-full-2024.xsd
│ ├── us-gaap-core-2024.xsd
│ ├── us-gaap-banking-2024.xsd
│ ├── us-gaap-manufacturing-2024.xsd
│ ├── us-gaap-insurance-2024.xsd
│ └── ... (hundreds of other files)
└── linkbases/
└── ... (thousands of files)
User faces questions:
Result: Manual configuration, error-prone, time-consuming
TaxonomyPackageReader reader = new TaxonomyPackageReader();
TaxonomyPackageInfo info = reader.readPackage("US-GAAP-2024.zip");
// Tool automatically discovers and presents entry points
System.out.println("Available taxonomies:");
for (EntryPoint ep : info.getEntryPoints()) {
System.out.println(" - " + ep.getName());
System.out.println(" " + ep.getDescription());
System.out.println(" Version: " + ep.getVersion());
}
// Output:
// Available taxonomies:
// - Full US GAAP
// Complete US GAAP taxonomy including all elements...
// Version: 2024.1.0
// - Core Concepts Only
// Core US GAAP concepts without industry extensions
// Version: 2024.1.0
// - Banking and Financial Services
// US GAAP with banking extensions
// Version: 2024.1.0
Result: Automatic discovery, self-documenting, user-friendly
<?xml version="1.0" encoding="UTF-8"?>
<tp:taxonomyPackage xmlns:tp="http://xbrl.org/2016/taxonomy-package">
<tp:identifier>http://fasb.org/us-gaap/2024</tp:identifier>
<tp:name>US GAAP Taxonomy 2024</tp:name>
<tp:version>2024.1.0</tp:version>
<tp:entryPoints>
<!-- Entry Point 1: Complete Taxonomy (Recommended) -->
<tp:entryPoint>
<tp:name>Full US GAAP</tp:name>
<tp:description>
Complete US GAAP taxonomy including all elements,
all linkbases, and all industry extensions.
This is the recommended entry point for most users.
</tp:description>
<tp:version>2024.1.0</tp:version>
<tp:entryPointDocument
href="http://fasb.org/us-gaap/2024/elts/us-gaap-2024.xsd"/>
</tp:entryPoint>
<!-- Entry Point 2: Core Only -->
<tp:entryPoint>
<tp:name>Core Concepts Only</tp:name>
<tp:description>
Core US GAAP concepts without industry-specific extensions.
Use this for general-purpose reporting that doesn't require
industry-specific elements.
</tp:description>
<tp:version>2024.1.0</tp:version>
<tp:entryPointDocument
href="http://fasb.org/us-gaap/2024/elts/us-gaap-core-2024.xsd"/>
</tp:entryPoint>
<!-- Entry Point 3: Industry-Specific - Banking -->
<tp:entryPoint>
<tp:name>Banking and Financial Services</tp:name>
<tp:description>
US GAAP with banking and financial services extensions.
Includes specialized concepts for banks, credit unions,
and other financial institutions.
</tp:description>
<tp:version>2024.1.0</tp:version>
<tp:entryPointDocument
href="http://fasb.org/us-gaap/2024/industry/us-gaap-banking-2024.xsd"/>
</tp:entryPoint>
<!-- Entry Point 4: Industry-Specific - Insurance -->
<tp:entryPoint>
<tp:name>Insurance</tp:name>
<tp:description>
US GAAP with insurance industry extensions.
Includes specialized concepts for life, health, and
property/casualty insurance companies.
</tp:description>
<tp:version>2024.1.0</tp:version>
<tp:entryPointDocument
href="http://fasb.org/us-gaap/2024/industry/us-gaap-insurance-2024.xsd"/>
</tp:entryPoint>
<!-- Entry Point 5: Deprecated Elements -->
<tp:entryPoint>
<tp:name>Including Deprecated Elements</tp:name>
<tp:description>
Full US GAAP including deprecated elements from prior versions.
Use this only for reading historical filings.
Do NOT use for new filings.
</tp:description>
<tp:version>2024.1.0</tp:version>
<tp:entryPointDocument
href="http://fasb.org/us-gaap/2024/elts/us-gaap-deprecated-2024.xsd"/>
</tp:entryPoint>
</tp:entryPoints>
</tp:taxonomyPackage>
Some entry points need to reference multiple starting files:
<tp:entryPoint>
<tp:name>Custom Reporting Bundle</tp:name>
<tp:description>
Custom entry point that loads multiple schema files
and specific linkbases for specialized reporting.
</tp:description>
<tp:version>2024.1.0</tp:version>
<!-- Multiple entry point documents -->
<tp:entryPointDocument
href="http://example.com/taxonomy/2024/core/concepts.xsd"/>
<tp:entryPointDocument
href="http://example.com/taxonomy/2024/extensions/custom-concepts.xsd"/>
<tp:entryPointDocument
href="http://example.com/taxonomy/2024/linkbases/custom-calculations.xml"/>
</tp:entryPoint>
Use Case: When DTS discovery should start from multiple files simultaneously
<tp:entryPoint>
<!-- Names in multiple languages -->
<tp:name xml:lang="en">Full Taxonomy</tp:name>
<tp:name xml:lang="fr">Taxonomie Complète</tp:name>
<tp:name xml:lang="de">Vollständige Taxonomie</tp:name>
<tp:name xml:lang="es">Taxonomía Completa</tp:name>
<!-- Descriptions in multiple languages -->
<tp:description xml:lang="en">
Complete taxonomy including all concepts and linkbases
</tp:description>
<tp:description xml:lang="fr">
Taxonomie complète incluant tous les concepts et linkbases
</tp:description>
<tp:description xml:lang="de">
Vollständige Taxonomie mit allen Konzepten und Linkbases
</tp:description>
<tp:description xml:lang="es">
Taxonomía completa incluyendo todos los conceptos y linkbases
</tp:description>
<tp:version>2024.1.0</tp:version>
<tp:entryPointDocument
href="http://example.com/taxonomy/2024/full-taxonomy.xsd"/>
</tp:entryPoint>
Tools select appropriate language based on user preferences.
Different entry points can have different versions:
<tp:taxonomyPackage xmlns:tp="http://xbrl.org/2016/taxonomy-package">
<!-- Package overall version -->
<tp:identifier>http://example.com/taxonomy/2024</tp:identifier>
<tp:version>2024.1.0</tp:version>
<tp:entryPoints>
<!-- Core module - version 2024.1.0 -->
<tp:entryPoint>
<tp:name>Core Module</tp:name>
<tp:version>2024.1.0</tp:version>
<tp:entryPointDocument href="..."/>
</tp:entryPoint>
<!-- Banking extension - version 2024.2.0 (updated more recently) -->
<tp:entryPoint>
<tp:name>Banking Extension</tp:name>
<tp:version>2024.2.0</tp:version>
<tp:entryPointDocument href="..."/>
</tp:entryPoint>
<!-- Insurance extension - version 2024.1.1 (minor update) -->
<tp:entryPoint>
<tp:name>Insurance Extension</tp:name>
<tp:version>2024.1.1</tp:version>
<tp:entryPointDocument href="..."/>
</tp:entryPoint>
</tp:entryPoints>
</tp:taxonomyPackage>
US GAAP taxonomy typically provides:
IFRS provides entry points for:
ESEF taxonomy structure:
Complex regulatory taxonomies often have:
public class EntryPointSelector {
public void selectAndLoadEntryPoint(File packageFile)
throws Exception {
// 1. Read package
TaxonomyPackageReader reader = new TaxonomyPackageReader();
TaxonomyPackageInfo pkgInfo = reader.readPackage(packageFile);
System.out.println("Taxonomy Package: " + pkgInfo.getName());
System.out.println("Version: " + pkgInfo.getVersion());
System.out.println("Publisher: " + pkgInfo.getPublisher());
System.out.println();
// 2. Get entry points
List<EntryPoint> entryPoints = pkgInfo.getEntryPoints();
if (entryPoints.isEmpty()) {
System.out.println("WARNING: No entry points defined.");
System.out.println("Manual schema selection required.");
return;
}
// 3. Present to user
System.out.println("Available Entry Points:");
System.out.println("======================");
for (int i = 0; i < entryPoints.size(); i++) {
EntryPoint ep = entryPoints.get(i);
System.out.printf("%n%d. %s", i + 1, ep.getName());
if (ep.getVersion() != null) {
System.out.printf(" (v%s)", ep.getVersion());
}
System.out.println();
if (ep.getDescription() != null) {
// Word wrap description
String wrapped = wordWrap(ep.getDescription(), 70);
System.out.println(" " + wrapped.replace("\n", "\n "));
}
System.out.println(" Entry Point Documents:");
for (String docHref : ep.getDocumentHrefs()) {
System.out.println(" - " + docHref);
}
}
// 4. User selects (or default to first)
System.out.println();
int selection = getUserSelection(entryPoints.size());
EntryPoint selected = entryPoints.get(selection);
System.out.printf("%nLoading: %s%n", selected.getName());
System.out.println("=".repeat(50));
// 5. Load taxonomy from entry point
loadEntryPoint(selected, pkgInfo);
}
private void loadEntryPoint(EntryPoint entryPoint,
TaxonomyPackageInfo pkgInfo)
throws Exception {
DTS dts = new DTS();
CatalogRemapper remapper = pkgInfo.getRemapper();
for (String docHref : entryPoint.getDocumentHrefs()) {
// Apply URL remapping
String localPath = remapper.remapUrl(docHref);
System.out.printf(" Loading: %s%n", localPath);
System.out.printf(" (remapped from: %s)%n", docHref);
// Load schema or linkbase
if (localPath.endsWith(".xsd")) {
dts.loadSchema(localPath, remapper);
} else if (localPath.endsWith(".xml")) {
dts.loadLinkbase(localPath, remapper);
} else {
System.out.println(" WARNING: Unknown file type");
}
}
System.out.println();
System.out.println("Taxonomy loaded successfully!");
System.out.printf(" Total concepts: %d%n", dts.getConceptCount());
System.out.printf(" Total relationships: %d%n",
dts.getRelationshipCount());
}
private int getUserSelection(int maxOptions) {
Scanner scanner = new Scanner(System.in);
while (true) {
System.out.printf("%nSelect entry point (1-%d) [1]: ",
maxOptions);
String input = scanner.nextLine().trim();
// Default to first option
if (input.isEmpty()) {
return 0;
}
try {
int selection = Integer.parseInt(input);
if (selection >= 1 && selection <= maxOptions) {
return selection - 1; // Convert to 0-based
}
} catch (NumberFormatException e) {
// Invalid input, continue loop
}
System.out.println("Invalid selection. Please try again.");
}
}
private String wordWrap(String text, int width) {
// Simple word wrap implementation
StringBuilder result = new StringBuilder();
String[] words = text.split("\\s+");
int lineLength = 0;
for (String word : words) {
if (lineLength + word.length() + 1 > width) {
result.append("\n");
lineLength = 0;
}
if (lineLength > 0) {
result.append(" ");
lineLength++;
}
result.append(word);
lineLength += word.length();
}
return result.toString();
}
}
public class EntryPointSelectorGUI extends JFrame {
private TaxonomyPackageInfo packageInfo;
private JList<EntryPoint> entryPointList;
private JTextArea descriptionArea;
private JButton loadButton;
public EntryPointSelectorGUI(TaxonomyPackageInfo pkgInfo) {
this.packageInfo = pkgInfo;
setTitle("Select Taxonomy Entry Point - " + pkgInfo.getName());
setSize(600, 400);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
initComponents();
}
private void initComponents() {
setLayout(new BorderLayout(10, 10));
// Top panel - package info
JPanel topPanel = new JPanel(new GridLayout(3, 1));
topPanel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
topPanel.add(new JLabel("Taxonomy: " + packageInfo.getName()));
topPanel.add(new JLabel("Version: " + packageInfo.getVersion()));
topPanel.add(new JLabel("Publisher: " + packageInfo.getPublisher()));
add(topPanel, BorderLayout.NORTH);
// Center - split pane with list and description
JSplitPane splitPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT);
// Entry point list
DefaultListModel<EntryPoint> model = new DefaultListModel<>();
for (EntryPoint ep : packageInfo.getEntryPoints()) {
model.addElement(ep);
}
entryPointList = new JList<>(model);
entryPointList.setSelectionMode(
ListSelectionModel.SINGLE_SELECTION);
entryPointList.setCellRenderer(new EntryPointRenderer());
entryPointList.addListSelectionListener(e -> {
if (!e.getValueIsAdjusting()) {
updateDescription();
}
});
JScrollPane listScroll = new JScrollPane(entryPointList);
listScroll.setBorder(BorderFactory.createTitledBorder(
"Available Entry Points"));
splitPane.setTopComponent(listScroll);
// Description area
descriptionArea = new JTextArea();
descriptionArea.setEditable(false);
descriptionArea.setWrapStyleWord(true);
descriptionArea.setLineWrap(true);
JScrollPane descScroll = new JScrollPane(descriptionArea);
descScroll.setBorder(BorderFactory.createTitledBorder(
"Description"));
splitPane.setBottomComponent(descScroll);
splitPane.setDividerLocation(200);
add(splitPane, BorderLayout.CENTER);
// Bottom - load button
JPanel bottomPanel = new JPanel();
loadButton = new JButton("Load Selected Entry Point");
loadButton.addActionListener(e -> loadSelectedEntryPoint());
bottomPanel.add(loadButton);
add(bottomPanel, BorderLayout.SOUTH);
// Select first entry point by default
if (!packageInfo.getEntryPoints().isEmpty()) {
entryPointList.setSelectedIndex(0);
}
}
private void updateDescription() {
EntryPoint selected = entryPointList.getSelectedValue();
if (selected != null) {
StringBuilder sb = new StringBuilder();
sb.append("Name: ").append(selected.getName()).append("\n\n");
if (selected.getVersion() != null) {
sb.append("Version: ").append(selected.getVersion())
.append("\n\n");
}
if (selected.getDescription() != null) {
sb.append(selected.getDescription()).append("\n\n");
}
sb.append("Entry Point Documents:\n");
for (String href : selected.getDocumentHrefs()) {
sb.append(" • ").append(href).append("\n");
}
descriptionArea.setText(sb.toString());
descriptionArea.setCaretPosition(0);
}
}
private void loadSelectedEntryPoint() {
EntryPoint selected = entryPointList.getSelectedValue();
if (selected == null) {
JOptionPane.showMessageDialog(this,
"Please select an entry point first.",
"No Selection",
JOptionPane.WARNING_MESSAGE);
return;
}
// Load in background thread
SwingWorker<Void, String> worker = new SwingWorker<>() {
@Override
protected Void doInBackground() throws Exception {
publish("Loading: " + selected.getName());
// Load taxonomy
// ... implementation ...
return null;
}
@Override
protected void process(List<String> chunks) {
for (String msg : chunks) {
System.out.println(msg);
}
}
@Override
protected void done() {
try {
get(); // Check for exceptions
JOptionPane.showMessageDialog(
EntryPointSelectorGUI.this,
"Taxonomy loaded successfully!",
"Success",
JOptionPane.INFORMATION_MESSAGE);
} catch (Exception e) {
JOptionPane.showMessageDialog(
EntryPointSelectorGUI.this,
"Error loading taxonomy: " + e.getMessage(),
"Error",
JOptionPane.ERROR_MESSAGE);
}
}
};
worker.execute();
}
// Custom renderer for entry point list
private static class EntryPointRenderer
extends DefaultListCellRenderer {
@Override
public Component getListCellRendererComponent(
JList<?> list, Object value, int index,
boolean isSelected, boolean cellHasFocus) {
super.getListCellRendererComponent(
list, value, index, isSelected, cellHasFocus);
if (value instanceof EntryPoint) {
EntryPoint ep = (EntryPoint) value;
String text = ep.getName();
if (ep.getVersion() != null) {
text += " (v" + ep.getVersion() + ")";
}
setText(text);
}
return this;
}
}
}
1. Always Provide Entry Points
<!-- DON'T: Empty or missing entry points -->
<tp:taxonomyPackage>
<!-- No entry points defined -->
</tp:taxonomyPackage>
<!-- DO: Clear, documented entry points -->
<tp:taxonomyPackage>
<tp:entryPoints>
<tp:entryPoint>
<tp:name>Recommended Entry Point</tp:name>
<tp:description>...</tp:description>
<tp:entryPointDocument href="..."/>
</tp:entryPoint>
</tp:entryPoints>
</tp:taxonomyPackage>
2. Order by Importance
<!-- First entry point = recommended/default -->
<tp:entryPoints>
<tp:entryPoint>
<tp:name>Full Taxonomy (Recommended)</tp:name>
<!-- Most users should use this -->
</tp:entryPoint>
<tp:entryPoint>
<tp:name>Advanced Options</tp:name>
<!-- For special cases -->
</tp:entryPoint>
</tp:entryPoints>
3. Provide Clear Descriptions
<!-- DON'T: Vague description -->
<tp:description>This is an entry point</tp:description>
<!-- DO: Helpful description -->
<tp:description>
Complete US GAAP taxonomy including all standard elements,
all linkbases, and industry extensions. Recommended for
SEC filers and general-purpose financial reporting.
</tp:description>
4. Use Descriptive Names
<!-- DON'T: Cryptic names -->
<tp:name>EP1</tp:name>
<tp:name>Full</tp:name>
<tp:name>Option A</tp:name>
<!-- DO: Clear names -->
<tp:name>Full US GAAP Taxonomy</tp:name>
<tp:name>Core Concepts Only</tp:name>
<tp:name>Banking Industry Extension</tp:name>
5. Multi-Language Support
<!-- For international taxonomies -->
<tp:entryPoint>
<tp:name xml:lang="en">Full Taxonomy</tp:name>
<tp:name xml:lang="fr">Taxonomie Complète</tp:name>
<tp:description xml:lang="en">...</tp:description>
<tp:description xml:lang="fr">...</tp:description>
</tp:entryPoint>
1. Always Present Entry Points
2. Respect Document Order
3. Handle Missing Entry Points
4. Show Metadata
5. Remember User Preferences
| Aspect | Without Entry Points | With Entry Points |
|---|---|---|
| Discovery | Manual ZIP exploration | Automatic |
| Documentation | External files needed | Self-contained |
| User Experience | Complex, error-prone | Simple, guided |
| Tool Setup | Manual configuration | Automatic |
| Multi-option Support | Unclear which to use | Clear choices |
| Standardization | Tool-specific | Standard across tools |
| Versioning | Hard to track | Per-entry-point versions |
| Multi-language | Not supported | Built-in |
| Best Practices | Varies by publisher | Consistent approach |
URL remapping allows:
<?xml version="1.0" encoding="UTF-8"?>
<catalog xmlns="urn:oasis:names:tc:entity:xmlns:xml:catalog">
<!-- Remap entire domain -->
<rewriteURI
uriStartString="http://example.com/taxonomy/2024/"
rewritePrefix="../taxonomy/"/>
<!-- Remap specific path -->
<rewriteURI
uriStartString="http://xbrl.org/2003/instance"
rewritePrefix="../xbrl-spec/"/>
<!-- Multiple remappings possible -->
<rewriteURI
uriStartString="http://www.xbrl.org/2005/xbrldt"
rewritePrefix="../xbrl-dimensions/"/>
</catalog>
URL Resolution Request
Processor needs: http://example.com/taxonomy/2024/concepts.xsd
Check Remappings (in order)
Match found: "http://example.com/taxonomy/2024/"
Apply Remapping
Replace prefix with: "../taxonomy/"
Result: ../taxonomy/concepts.xsd
Resolve Relative Path
Relative to package location
Final: TaxonomyPackage-v1.0/taxonomy/concepts.xsd
Order Matters:
String Matching:
Relative Paths:
rewritePrefix can be relative../Published Taxonomy URLs:
http://example.com/taxonomy/2024/core/concepts.xsd
http://example.com/taxonomy/2024/core/labels.xml
http://example.com/taxonomy/2024/extensions/industry.xsd
Package Structure:
ExampleTaxonomy-2024/
├── META-INF/
│ ├── taxonomyPackage.xml
│ └── catalog.xml
└── files/
├── core/
│ ├── concepts.xsd
│ └── labels.xml
└── extensions/
└── industry.xsd
catalog.xml:
<catalog xmlns="urn:oasis:names:tc:entity:xmlns:xml:catalog">
<rewriteURI
uriStartString="http://example.com/taxonomy/2024/"
rewritePrefix="../files/"/>
</catalog>
Result:
All http://example.com/taxonomy/2024/ URLs resolve to local files under files/ directory.
.taxonomyPackage.xml filename (with leading dot)http://www.corefiling.com/xbrl/taxonomypackage/v1.taxonomyPackage.xmltaxonomyPackage.xml (no leading dot)http://xbrl.org/2016/taxonomy-package| Aspect | v1.0.2 (Eurofiling) | 1.0 (XBRL Intl) |
|---|---|---|
| Filename | .taxonomyPackage.xml |
taxonomyPackage.xml |
| Namespace | http://www.corefiling.com/xbrl/taxonomypackage/v1 |
http://xbrl.org/2016/taxonomy-package |
| Location | Flexible | Must be in META-INF/ |
| Remapping | <tp:remapping> elements |
catalog.xml (OASIS) |
| Schema | CoreFiling schema | XBRL International schema |
If you implemented v1.0.0-v1.0.2:
Backward Compatibility:
import javax.xml.parsers.*;
import org.w3c.dom.*;
import java.io.InputStream;
import java.util.zip.*;
public class TaxonomyPackageReader {
private static final String TP_NAMESPACE =
"http://xbrl.org/2016/taxonomy-package";
public TaxonomyPackageInfo readPackage(File zipFile)
throws Exception {
try (ZipFile zip = new ZipFile(zipFile)) {
// Find META-INF/taxonomyPackage.xml
ZipEntry metaInfEntry = findMetaInfEntry(zip);
if (metaInfEntry == null) {
throw new IllegalArgumentException(
"Not a valid taxonomy package: " +
"META-INF/taxonomyPackage.xml not found");
}
// Parse XML
InputStream is = zip.getInputStream(metaInfEntry);
Document doc = parseXml(is);
// Extract metadata
TaxonomyPackageInfo info = new TaxonomyPackageInfo();
Element root = doc.getDocumentElement();
info.setIdentifier(
getElementText(root, "identifier", TP_NAMESPACE));
info.setVersion(
getElementText(root, "version", TP_NAMESPACE));
info.setName(
getElementText(root, "name", TP_NAMESPACE));
info.setDescription(
getElementText(root, "description", TP_NAMESPACE));
info.setPublisher(
getElementText(root, "publisher", TP_NAMESPACE));
info.setPublicationDate(
getElementText(root, "publicationDate", TP_NAMESPACE));
// Extract entry points
NodeList entryPoints = root.getElementsByTagNameNS(
TP_NAMESPACE, "entryPoint");
for (int i = 0; i < entryPoints.getLength(); i++) {
Element ep = (Element) entryPoints.item(i);
EntryPoint entryPoint = parseEntryPoint(ep);
info.addEntryPoint(entryPoint);
}
// Load remappings from catalog.xml if present
CatalogRemapper remapper = loadCatalog(zip);
info.setRemapper(remapper);
return info;
}
}
private ZipEntry findMetaInfEntry(ZipFile zip) {
// Look for META-INF/taxonomyPackage.xml
// Handle top-level directory name variations
Enumeration<? extends ZipEntry> entries = zip.entries();
while (entries.hasMoreElements()) {
ZipEntry entry = entries.nextElement();
String name = entry.getName();
// Match: */META-INF/taxonomyPackage.xml
if (name.endsWith("META-INF/taxonomyPackage.xml")) {
return entry;
}
}
return null;
}
private EntryPoint parseEntryPoint(Element ep) {
EntryPoint entryPoint = new EntryPoint();
entryPoint.setName(
getElementText(ep, "name", TP_NAMESPACE));
entryPoint.setDescription(
getElementText(ep, "description", TP_NAMESPACE));
entryPoint.setVersion(
getElementText(ep, "version", TP_NAMESPACE));
// Entry point documents (may be multiple)
NodeList docs = ep.getElementsByTagNameNS(
TP_NAMESPACE, "entryPointDocument");
for (int i = 0; i < docs.getLength(); i++) {
Element doc = (Element) docs.item(i);
String href = doc.getAttribute("href");
entryPoint.addDocument(href);
}
return entryPoint;
}
private CatalogRemapper loadCatalog(ZipFile zip)
throws Exception {
// Look for META-INF/catalog.xml
ZipEntry catalogEntry = findCatalogEntry(zip);
if (catalogEntry == null) {
return new CatalogRemapper(); // Empty remapper
}
// Parse catalog.xml using OASIS XML Catalog parser
InputStream is = zip.getInputStream(catalogEntry);
return CatalogParser.parse(is);
}
private String getElementText(Element parent,
String localName,
String namespace) {
NodeList nodes = parent.getElementsByTagNameNS(
namespace, localName);
if (nodes.getLength() > 0) {
return nodes.item(0).getTextContent().trim();
}
return null;
}
}
import org.xml.sax.InputSource;
import javax.xml.catalog.*;
import java.net.URI;
public class CatalogRemapper {
private final CatalogResolver catalogResolver;
private final File packageRoot;
public CatalogRemapper(File catalogFile, File packageRoot)
throws Exception {
this.packageRoot = packageRoot;
// Use Java XML Catalog API (Java 9+)
this.catalogResolver = CatalogManager.catalogResolver(
CatalogFeatures.defaults(),
URI.create(catalogFile.toURI().toString()));
}
public String remapUrl(String publicUrl) {
try {
// Resolve using catalog
InputSource resolved = catalogResolver.resolveEntity(
null, publicUrl);
if (resolved != null && resolved.getSystemId() != null) {
String systemId = resolved.getSystemId();
// Convert to file path within package
URI uri = URI.create(systemId);
if ("file".equals(uri.getScheme())) {
return uri.getPath();
}
// Resolve relative to package root
File resolvedFile = new File(packageRoot, systemId);
return resolvedFile.getAbsolutePath();
}
// No remapping found, return original
return publicUrl;
} catch (Exception e) {
// Error in remapping, return original URL
return publicUrl;
}
}
}
public class TaxonomyPackageInfo {
private String identifier;
private String version;
private String name;
private String description;
private String publisher;
private String publisherURL;
private String publicationDate;
private List<String> supersededPackages;
private List<EntryPoint> entryPoints;
private CatalogRemapper remapper;
// Getters and setters...
}
public class EntryPoint {
private String name;
private String description;
private String version;
private List<String> documentHrefs;
// Getters and setters...
}
public class TaxonomyLoader {
public void loadTaxonomy(File packageFile) throws Exception {
// Read package
TaxonomyPackageReader reader = new TaxonomyPackageReader();
TaxonomyPackageInfo pkgInfo = reader.readPackage(packageFile);
System.out.println("Loading: " + pkgInfo.getName());
System.out.println("Version: " + pkgInfo.getVersion());
System.out.println("Publisher: " + pkgInfo.getPublisher());
// List entry points
System.out.println("\nAvailable entry points:");
for (EntryPoint ep : pkgInfo.getEntryPoints()) {
System.out.println(" - " + ep.getName());
System.out.println(" " + ep.getDescription());
for (String docHref : ep.getDocumentHrefs()) {
// Apply URL remapping
String localPath = pkgInfo.getRemapper()
.remapUrl(docHref);
System.out.println(" Document: " + localPath);
}
}
// Load taxonomy using first entry point
if (!pkgInfo.getEntryPoints().isEmpty()) {
EntryPoint primary = pkgInfo.getEntryPoints().get(0);
loadTaxonomyFromEntryPoint(primary, pkgInfo.getRemapper());
}
}
private void loadTaxonomyFromEntryPoint(
EntryPoint entryPoint,
CatalogRemapper remapper) throws Exception {
for (String docHref : entryPoint.getDocumentHrefs()) {
String localPath = remapper.remapUrl(docHref);
// Load schema file
DTS dts = new DTS();
dts.loadSchemaRef(localPath, remapper);
// DTS discovery will use remapper for all subsequent URLs
}
}
}
Report Packages (Recommendation 2025) extend Taxonomy Packages to bundle:
Problems Solved:
Use Cases:
CompanyReport-2024-Q4.xbri (Inline XBRL report package)
└── CompanyReport-2024-Q4/ (top-level directory)
├── META-INF/
│ ├── reportPackage.json (required for .xbri/.xbr)
│ ├── taxonomyPackage.xml (if includes taxonomy)
│ └── catalog.xml (for URL remapping)
├── reports/ (required)
│ ├── annual-report.xhtml (Inline XBRL report)
│ ├── logo.png (supporting image)
│ ├── styles.css (styling)
│ └── fonts/
│ └── custom-font.woff
└── taxonomy/ (extension taxonomy)
├── company-extension.xsd
├── company-labels.xml
└── company-presentation.xml
File Extension: .xbri
Contains: Single Inline XBRL Document or Document Set
Example:
AcmeCorp-2024-Annual.xbri
└── AcmeCorp-2024-Annual/
├── META-INF/
│ └── reportPackage.json
└── reports/
└── annual-report.xhtml (single iXBRL document)
File Extension: .xbr
Contains: xBRL-XML, xBRL-JSON, or xBRL-CSV report
Example (xBRL-XML):
AcmeCorp-2024-Q1.xbr
└── AcmeCorp-2024-Q1/
├── META-INF/
│ └── reportPackage.json
└── reports/
└── q1-report.xbrl (XBRL instance)
Example (xBRL-CSV):
BankLoanData-2024-12.xbr
└── BankLoanData-2024-12/
├── META-INF/
│ └── reportPackage.json
└── reports/
├── metadata.json (CSV metadata)
└── loan-data.csv (CSV data)
File Extension: .zip
Contains: Any number of reports
Example:
MultipleReports-2024.zip
└── MultipleReports-2024/
├── META-INF/
│ └── reportPackage.json (optional)
└── reports/
├── q1-report.xhtml
├── q2-report.xhtml
├── q3-report.xhtml
└── q4-report.xhtml
{
"documentInfo": {
"documentType": "https://xbrl.org/report-package/2023/xbri"
}
}
Document Types:
https://xbrl.org/report-package/2023/xbri - Inline XBRL package (.xbri)https://xbrl.org/report-package/2023/xbr - Non-Inline package (.xbr)https://xbrl.org/report-package/2023 - Legacy/unconstrained (.zip)Validation:
A report package can also be a taxonomy package:
CompanyFiling-2024.xbri
└── CompanyFiling-2024/
├── META-INF/
│ ├── reportPackage.json (Report Package metadata)
│ ├── taxonomyPackage.xml (Taxonomy Package metadata)
│ └── catalog.xml (URL remapping)
├── reports/
│ └── annual-report.xhtml
└── taxonomy/
├── extension.xsd
└── labels.xml
Benefits:
Multiple HTML files forming single report:
CompanyReport-2024.xbri
└── CompanyReport-2024/
├── META-INF/
│ └── reportPackage.json
└── reports/
└── financials/ (subdirectory = document set)
├── balance-sheet.xhtml
├── income-statement.xhtml
├── cash-flows.xhtml
└── notes.xhtml
Rules:
.xhtml files in subdirectory = Document Setpublic class ReportPackageReader {
private static final String REPORT_DIR = "reports";
private static final String META_INF = "META-INF";
public ReportPackageInfo readReportPackage(File zipFile)
throws Exception {
try (ZipFile zip = new ZipFile(zipFile)) {
// Find top-level directory
String topLevelDir = findTopLevelDirectory(zip);
// Read reportPackage.json
String reportPackageJson = topLevelDir +
META_INF +
"/reportPackage.json";
ZipEntry jsonEntry = zip.getEntry(reportPackageJson);
ReportPackageInfo info = new ReportPackageInfo();
if (jsonEntry != null) {
String json = readEntry(zip, jsonEntry);
parseReportPackageJson(json, info);
}
// Check if also taxonomy package
String taxonomyPackageXml = topLevelDir +
META_INF +
"/taxonomyPackage.xml";
ZipEntry taxEntry = zip.getEntry(taxonomyPackageXml);
if (taxEntry != null) {
// Also a taxonomy package
TaxonomyPackageInfo taxInfo =
readTaxonomyPackage(zip, topLevelDir);
info.setTaxonomyPackage(taxInfo);
}
// Find reports
List<Report> reports = findReports(zip, topLevelDir);
info.setReports(reports);
return info;
}
}
private List<Report> findReports(ZipFile zip, String topLevelDir) {
List<Report> reports = new ArrayList<>();
String reportPrefix = topLevelDir + REPORT_DIR + "/";
// Group entries by directory
Map<String, List<ZipEntry>> byDirectory = new HashMap<>();
Enumeration<? extends ZipEntry> entries = zip.entries();
while (entries.hasMoreElements()) {
ZipEntry entry = entries.nextElement();
String name = entry.getName();
if (name.startsWith(reportPrefix) && !entry.isDirectory()) {
String relativePath = name.substring(reportPrefix.length());
int slashIndex = relativePath.indexOf('/');
String directory = slashIndex > 0
? relativePath.substring(0, slashIndex)
: "";
byDirectory.computeIfAbsent(directory,
k -> new ArrayList<>()).add(entry);
}
}
// Process each directory
for (Map.Entry<String, List<ZipEntry>> dirEntry :
byDirectory.entrySet()) {
String dir = dirEntry.getKey();
List<ZipEntry> files = dirEntry.getValue();
if (dir.isEmpty()) {
// Root level reports - each file is separate report
for (ZipEntry file : files) {
Report report = identifyReport(file);
if (report != null) {
reports.add(report);
}
}
} else {
// Subdirectory - might be Document Set
if (isInlineXBRLDocumentSet(files)) {
Report report = new Report();
report.setType(ReportType.INLINE_XBRL_DOCUMENT_SET);
report.setFiles(files);
reports.add(report);
} else {
// Individual reports in subdirectory
for (ZipEntry file : files) {
Report report = identifyReport(file);
if (report != null) {
reports.add(report);
}
}
}
}
}
// Sort by filename for consistent ordering
reports.sort(Comparator.comparing(Report::getName));
return reports;
}
private Report identifyReport(ZipEntry entry) {
String name = entry.getName().toLowerCase();
Report report = new Report();
report.setEntry(entry);
if (name.endsWith(".xhtml") ||
name.endsWith(".html") ||
name.endsWith(".htm")) {
report.setType(ReportType.INLINE_XBRL);
} else if (name.endsWith(".xbrl")) {
report.setType(ReportType.XBRL_XML);
} else if (name.endsWith(".json")) {
// Could be xBRL-JSON or xBRL-CSV metadata
report.setType(ReportType.JSON_BASED);
} else {
// Not a recognized report
return null;
}
return report;
}
private boolean isInlineXBRLDocumentSet(List<ZipEntry> files) {
// Document set: multiple .xhtml/.html/.htm files
int htmlCount = 0;
for (ZipEntry file : files) {
String name = file.getName().toLowerCase();
if (name.endsWith(".xhtml") ||
name.endsWith(".html") ||
name.endsWith(".htm")) {
htmlCount++;
}
}
return htmlCount > 1;
}
}
2018: Working Group Note on including reports in taxonomy packages
2024: Report Packages specification development
December 2025: Report Packages 1.0 Recommendation released
Before Report Packages:
With Report Packages:
# 1. Create directory structure
mkdir MyTaxonomy-1.0
cd MyTaxonomy-1.0
# 2. Create META-INF
mkdir META-INF
# 3. Create taxonomyPackage.xml
cat > META-INF/taxonomyPackage.xml << EOF
<?xml version="1.0" encoding="UTF-8"?>
<tp:taxonomyPackage xmlns:tp="http://xbrl.org/2016/taxonomy-package">
<tp:identifier>http://example.com/taxonomy/1.0</tp:identifier>
<tp:name>My Taxonomy</tp:name>
<tp:version>1.0</tp:version>
<tp:entryPoints>
<tp:entryPoint>
<tp:name>Main Entry Point</tp:name>
<tp:entryPointDocument href="http://example.com/taxonomy/1.0/main.xsd"/>
</tp:entryPoint>
</tp:entryPoints>
</tp:taxonomyPackage>
EOF
# 4. Copy taxonomy files
cp -r /path/to/taxonomy/* ./
# 5. Create ZIP
cd ..
zip -r MyTaxonomy-1.0.zip MyTaxonomy-1.0/
public class TaxonomyPackageBuilder {
private File outputZip;
private String topLevelDir;
private TaxonomyPackageInfo metadata;
public TaxonomyPackageBuilder(String name, String version) {
this.topLevelDir = name + "-" + version;
this.outputZip = new File(topLevelDir + ".zip");
this.metadata = new TaxonomyPackageInfo();
metadata.setName(name);
metadata.setVersion(version);
}
public void addTaxonomyFiles(File taxonomyDir) throws IOException {
// Copy taxonomy files to package
// ...
}
public void addEntryPoint(String name, String href) {
EntryPoint ep = new EntryPoint();
ep.setName(name);
ep.addDocument(href);
metadata.addEntryPoint(ep);
}
public void addRemapping(String publicUrl, String localPath) {
// Add to catalog.xml
// ...
}
public void build() throws Exception {
try (ZipOutputStream zos = new ZipOutputStream(
new FileOutputStream(outputZip))) {
// Write taxonomyPackage.xml
String xmlPath = topLevelDir + "/META-INF/taxonomyPackage.xml";
ZipEntry entry = new ZipEntry(xmlPath);
zos.putNextEntry(entry);
writeTaxonomyPackageXml(zos, metadata);
zos.closeEntry();
// Write catalog.xml if remappings exist
if (metadata.hasRemappings()) {
String catalogPath = topLevelDir + "/META-INF/catalog.xml";
ZipEntry catalogEntry = new ZipEntry(catalogPath);
zos.putNextEntry(catalogEntry);
writeCatalogXml(zos, metadata.getRemappings());
zos.closeEntry();
}
// Write taxonomy files
writeTaxonomyFiles(zos);
}
}
}
public class TaxonomyPackageValidator {
public ValidationResult validate(File packageFile) {
ValidationResult result = new ValidationResult();
try (ZipFile zip = new ZipFile(packageFile)) {
// Check: Single top-level directory
if (!hasSingleTopLevelDirectory(zip)) {
result.addError("Must have single top-level directory");
}
// Check: META-INF/taxonomyPackage.xml exists
ZipEntry metaInf = findEntry(zip,
"META-INF/taxonomyPackage.xml");
if (metaInf == null) {
result.addError(
"Missing META-INF/taxonomyPackage.xml");
return result;
}
// Check: taxonomyPackage.xml is valid
Document doc = parseXml(zip.getInputStream(metaInf));
if (!isValidTaxonomyPackageXml(doc)) {
result.addError("Invalid taxonomyPackage.xml");
}
// Check: Entry points reference existing files
List<String> entryPointHrefs = extractEntryPointHrefs(doc);
for (String href : entryPointHrefs) {
// After remapping, file should exist
if (!fileExistsInPackage(zip, href)) {
result.addWarning(
"Entry point not found: " + href);
}
}
// Check: catalog.xml is valid (if present)
ZipEntry catalog = findEntry(zip, "META-INF/catalog.xml");
if (catalog != null) {
if (!isValidCatalogXml(
zip.getInputStream(catalog))) {
result.addError("Invalid catalog.xml");
}
}
} catch (Exception e) {
result.addError("Error reading package: " + e.getMessage());
}
return result;
}
}
Problem: URLs not resolving to local files
Solutions:
Problem: Tools can't find entry point schemas
Solutions:
<tp:entryPointDocument>Problem: Package has multiple top-level directories
Solutions:
Problem: Old tools don't recognize new format
Solutions:
.taxonomyPackage.xmlConsistent Naming
MyTax-2024.zipClear Entry Points
Comprehensive Metadata
Offline Support
Documentation
Support Both Formats
Robust Parsing
User Experience
Validation
Caching
| Tool | Taxonomy Package | Report Package | Notes |
|---|---|---|---|
| Arelle | ✅ Full | ✅ Full | Reference implementation |
| Altova | ✅ Full | ✅ Partial | Commercial tools |
| XBRL US | ✅ Full | ✅ Planned | Validator |
| XBRL International | ✅ Full | ✅ Full | Reference specs |
| GLOMIDCO 2014-2016 Processor | ⚠️ Partial | ⚠️ Partial | Needs update |
Recommendation for XBRL Processor:
Taxonomy Packages (2016 REC)
META-INF/taxonomyPackage.xmlcatalog.xmlReport Packages (2025 REC)
META-INF/reportPackage.json for type identificationImplementation Priorities
If you implemented Taxonomy Packages 2014-2016:
If you never implemented Taxonomy Packages:
Estimated Effort:
This document covers all aspects of Taxonomy and Report Packages as of January 2026, including historical evolution, technical details, and practical implementation guidance.