LXR Package Internals: Entrypoints, Schema Remapping, and Composition
This post dives into the advanced capabilities of LXR packages: how entrypoints drive automatic dependency resolution, how schema location remapping handles real-world schema brokenness, and how package composition enables reuse across projects.
Entrypoints and Automatic Dependency Resolution
The Import Graph Problem
When you declare a schema file in an LXR package configuration, that file is an entrypoint — not the full picture. XSD schemas declare import and include statements pointing to other schemas, which in turn import others:
┌─────────────────────────────────────────────────────────────────────┐
│ DEPENDENCY RESOLUTION │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ user declares automatic traversal all schemas │
│ ─────────── ────────────────────── ──────────── │
│ │
│ urbanFunction.xsd ──► reads import ──► urbanObject.xsd │
│ ──► reads import ──► gml.xsd │
│ │ │
│ ┌────────┴────────┐ │
│ │ gml.xsd imports │ │
│ │ • xlink.xsd │ │
│ │ • gmlBase.xsd │ │
│ │ • basicTypes.xsd│ │
│ └────────┬────────┘ │
│ │ │
│ ┌────────┴────────┐ │
│ │ gmlBase.xsd │ │
│ │ imports │ │
│ │ • coordinateSystems │ │
│ │ • datums.xsd │ │
│ │ • ... │ │
│ └────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────┘
LXR packages automatically traverse this entire graph starting from your entrypoints, collecting every referenced schema.
How It Works
In XsdBundler.collect_all_xsds(), the process is:
- Start with entrypoints — files declared in
files:in your config - Parse each schema — extract all
importandincludestatements - Follow each reference — resolve the location via schema location mappings
- Repeat until exhausted — no new schemas found
- Rewrite schemaLocation — update import/include paths to work in the package
The key is that the SchemaRepository tracks all processed schemas — every schema it has encountered during parsing, along with its resolved location. The bundler collects from this complete set.
# Simplified flow in XsdBundler
all_schemas = repository.send(:get_all_processed_schemas)
# all_schemas now contains EVERY schema in the graph, keyed by resolved location
Why Entrypoints Matter
An entrypoint is the root of a schema ecosystem. From one entrypoint like urbanFunction.xsd, the resolver can reach 50+ schemas across multiple standards (GML, ISO 19139, CityGML, XLink). You never need to manually declare every file — just the top-level schemas your application uses.
This means:
- Minimal configuration — declare only your entrypoints, not every dependency
- Complete bundling — the resolver ensures no referenced schema is missing
- Stable against schema updates — if a dependency updates its own imports, your package still captures everything
Multiple Entrypoints: Combining Schema Families
Some projects require multiple independent schema families in a single package. Consider Japan’s PLATEAU project, which combines:
- i-UR (urban renewal) schemas for urban function and urban objects
- FGD (Fundamental Geospatial Data) schemas for base mapping
- JPM (Japan Profile for Geographic Information Standards) schemas for metadata
Each schema family has its own entrypoint, imports different dependencies, and serves different purposes. Yet you want a unified documentation site that lets users search across all of them.
# config.yml for PLATEAU Schema Browser
files:
- schemas/i-ur/urbanFunction.xsd # i-UR urban function types
- schemas/i-ur/urbanObject.xsd # i-UR urban object types
- schemas/fgd/FGD_GMLSchema.xsd # Fundamental Geospatial Data
- schemas/jmp/jmp20.xsd # Japan Profile for Geographic Information Standards (JPGIS) Metadata
namespace_mappings:
- prefix: "urf"
uri: "https://www.geospatial.jp/iur/urf/3.2"
- prefix: "uro"
uri: "https://www.geospatial.jp/iur/uro/3.2"
- prefix: "fgd"
uri: "http://fgd.gsi.go.jp/spec/2008/FGD_GMLSchema"
- prefix: "gml"
uri: "http://www.opengis.net/gml/3.2"
- prefix: "jmp"
uri: "http://zgate.gsi.go.jp/ch/jmp/"
schema_location_mappings:
# Map GML HTTP URLs to local files
- from: '^http://schemas\.opengis\.net/gml/3\.1\.1/(?:base/)?(.+\.xsd)$'
to: schemas/codesynthesis-gml-3.2.1/gml/3.2.1/\1
pattern: true
- from: '^http://schemas\.opengis\.net/gml/3\.2\.1/(.+\.xsd)$'
to: schemas/codesynthesis-gml-3.2.1/gml/3.2.1/\1
pattern: true
When the parser processes these entrypoints:
- urbanFunction.xsd is parsed → imports GML → GML imports ISO schemas
- urbanObject.xsd is parsed → also imports GML (shared!)
- FGD_GMLSchema.xsd is parsed → imports GML (shared again!)
- jmp20.xsd is parsed → has its own dependencies
The key insight: schemas accumulate across entrypoint parses. Each entrypoint may reference shared schemas like GML, but GML is only parsed once and reused. The parsed schemas are cached and shared, avoiding duplicate work and ensuring consistency.
This produces a package with:
| Namespace | Types | Description |
|---|---|---|
urf |
481 | Urban function types |
uro |
621 | Urban object types |
fgd |
100 | Fundamental geospatial data |
gml |
866 | Geography Markup Language |
jmp |
132 | Japan Profile for Geographic Information Standards (JPGIS) Metadata |
| Total | 3014 | Across all schema families |
All types searchable from a single unified SPA documentation site.
Schema Location Remapping
Real-world schemas have broken, outdated, or intentionally modified import paths. LXR packages handle this through schema location remapping — redirecting any schema location to a local file of your choosing.
Why Remapping Is Necessary
Consider this real example from the i-UR configuration. The schema declares:
<import namespace="http://www.opengis.net/gml/3.1.1"
schemaLocation="http://schemas.opengis.net/gml/3.1.1/base/gml.xsd"/>
But GML 3.1.1 has a critical issue: its schema locations reference deprecated W3C URLs that now return errors. Specifically, W3C deprecated the www.w3.org namespace for XML Schema types, causing validation to fail. The solution: remap GML 3.1.1 URLs to GML 3.2.1 files, which are known to work.
# Map GML 3.1.1 HTTP URLs to GML 3.2.1 local files
- from: '^http://schemas\.opengis\.net/gml/3\.1\.1/(?:base/)?(.+\.xsd)$'
to: ../../spec/fixtures/codesynthesis-gml-3.2.1/gml/3.2.1/\1
pattern: true
This remapping rule says: “Any HTTP request for schemas.opengis.net/gml/3.1.1/*.xsd — redirect to the local GML 3.2.1 copy.”
Pattern-Based Remapping
Remapping rules support regular expressions for flexible matching:
schema_location_mappings:
# Map CityGML HTTP URLs to local files
- from: '^http://schemas\.opengis\.net/citygml/2\.0/(.+\.xsd)$'
to: schemas/citygml/2.0/\1
pattern: true
# Map ISO 19139 relative paths (gmd, gco, gss, gts, gmx, gsr)
- from: '(?:\.\./)+((?:gmd|gco|gss|gts|gmx|gsr)/(.+\.xsd))$'
to: schemas/codesynthesis-gml-3.2.1/iso/19139/20070417/\1
pattern: true
# Map bare ISO 19139 schema filenames
- from: '^(gmd|gco|gss|gts|gmx|gsr)\.xsd$'
to: schemas/codesynthesis-gml-3.2.1/iso/19139/20070417/\1/\1.xsd
pattern: true
How Remapping Works in the Bundler
┌──────────────────────────────────────────────────────────────────────┐
│ SCHEMA LOCATION REWRITING │
├──────────────────────────────────────────────────────────────────────┤
│ │
│ Original XSD content: │
│ ┌────────────────────────────────────────────────────────────────┐ │
│ │ <xs:import namespace="http://www.opengis.net/gml/3.2" │ │
│ │ schemaLocation="http://schemas.opengis.net/gml/3.1.1/ │ │
│ │ base/gml.xsd"/> │ │
│ └────────────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ schema_location_mappings: │
│ from: gml/3.1.1/base/gml.xsd │
│ to: gml/3.2.1/gml.xsd (local) │
│ │ │
│ ▼ │
│ Rewritten XSD content (in package): │
│ ┌────────────────────────────────────────────────────────────────┐ │
│ │ <xs:import namespace="http://www.opengis.net/gml/3.2" │ │
│ │ schemaLocation="gml.xsd"/> │ │
│ └────────────────────────────────────────────────────────────────┘ │
│ │
│ The bundled package uses flattened basenames, so all │
│ schemaLocation values point to the correct local file. │
│ │
└──────────────────────────────────────────────────────────────────────┘
The bundler (rewrite_schema_locations) reads each XSD file, finds all import and include elements, and rewrites their schemaLocation attributes to point to the correct file within the package.
Package Composition
LXR packages can be composed — multiple packages merged into a unified repository with automatic conflict resolution. This enables separation of concerns and reuse.
The Composition Model
┌─────────────────────────────────────────────────────────────────────┐
│ PACKAGE COMPOSITION │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ Package A: Package B: Package C: │
│ ┌─────────────┐ ┌─────────────┐ ┌───────────┐ │
│ │ GML 3.2.1 │ │ CityGML 2.0 │ │ i-UR 3.2 │ │
│ │ Schema Set │ │ Module Set │ │ Schema │ │
│ │ │ │ │ │ │ │
│ │ 40 schemas │ │ 15 schemas │ │ 10 schemas│ │
│ │ Types: 800 │ │ Types: 450 │ │ Types: 200│ │
│ └─────────────┘ └─────────────┘ └───────────┘ │
│ │ │ │ │
│ └────────────────────────┼─────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌──────────────────────────────┐ │
│ │ COMPOSED REPOSITORY │ │
│ │ │ │
│ │ All schemas merged │ │
│ │ Type index unified │ │
│ │ Namespace registry merged │ │
│ │ │ │
│ │ Conflict resolution: │ │
│ │ - Priority: B > A > C │ │
│ │ - Strategy: override │ │
│ │ │ │
│ └──────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────┘
Composition Configuration
# composition.yml
base_packages:
- package: schemas/gml-3.2.1.lxr
priority: 0
conflict_resolution: keep
- package: schemas/citygml-2.0.lxr
priority: 10
conflict_resolution: override
namespace_remapping:
- from_uri: "http://www.opengis.net/citygml/2.0"
to_uri: "http://example.com/citygml/2.0/modified"
- package: schemas/i-ur-3.2.lxr
priority: 5
conflict_resolution: keep
exclude_schemas:
- "urbanObject.xsd" # Already in GML package
Conflict Resolution Strategies
| Strategy | Behavior |
|---|---|
keep |
First package wins; duplicates allowed |
override |
Highest priority package wins |
error |
Raise error on conflict |
Namespace Remapping
When packages have overlapping URIs, use namespace remapping to disambiguate:
namespace_remapping:
- from_uri: "http://old.example.com/schema/v1"
to_uri: "http://new.example.com/schema/v1"
Advanced Package Queries
Once a package is loaded, you can query it deeply:
Type Hierarchy
# Show type inheritance chain
lutaml-xsd pkg type hierarchy gml:AbstractFeatureType urban_function.lxr
Dependency Analysis
# What types does BuildingType reference?
lutaml-xsd pkg type deps uro:BuildingType urban_function.lxr
# What types reference BuildingType (used_by)?
lutaml-xsd pkg type used-by uro:BuildingType urban_function.lxr
Element Coverage Analysis
# Analyze which types are reachable from entry point elements
lutaml-xsd pkg coverage urban_function.lxr --entry UrbanFunctionType
Summary
LXR packages provide sophisticated tooling for real-world schema challenges:
- Automatic dependency resolution — entrypoints drive full graph traversal
- Schema location remapping — redirect broken or outdated import paths to working files
- Package composition — merge packages with conflict resolution and namespace remapping
- Deep querying — type hierarchy, dependency analysis, coverage analysis
These capabilities enable you to build reliable, maintainable XML data pipelines even when working with complex, evolving schema standards.