Architecture
This guide explains the architecture and module structure of the Metaschema Java Tools.
The Metaschema Java Tools project provides a modular framework for working with Metaschema-based data models. Understanding the architecture helps you choose the right components for your use case and understand how the pieces fit together.
The framework is organized around five core capabilities:
- Loading and processing - Parse Metaschema module definitions and resolve imports
- Code generation - Generate Java classes and XML/JSON schemas from modules
- Data binding - Serialize and deserialize data in XML, JSON, and YAML formats
- Query execution - Evaluate Metapath expressions against loaded data
- Constraint validation - Validate data against Metaschema-defined rules
This section describes how the project is organized into Maven modules and Java packages.
The project is divided into several Maven modules, each with a specific responsibility. This separation allows you to include only what you need:
metaschema-java
├── metaschema-core # Core Metaschema model and Metapath
├── metaschema-model # Module loading and processing
├── metaschema-databind # Data binding framework
├── metaschema-schemagen # Schema generation (XSD, JSON Schema)
├── metaschema-cli # Command-line interface
└── metaschema-maven-plugin # Maven integration
Each module builds on the ones above it. The following sections describe what each module provides and show typical usage patterns.
The foundation layer that all other modules depend on. It provides:
- Metaschema model interfaces (
IModule,IAssemblyDefinition, etc.) - Metapath expression engine
- Constraint definitions
- Common utilities
// Core interfaces
IModule module;
IAssemblyDefinition assembly;
IFieldDefinition field;
IFlagDefinition flag;
IMetapathExpression expression;
Builds on metaschema-core to provide module loading and processing capabilities:
- Metaschema XML parser
- Module resolution and imports
- Definition lookup
MetaschemaLoader loader = new MetaschemaLoader();
IModule module = loader.load(path);
The data binding layer that most applications will use directly. This module handles:
- Serialization/deserialization
- Format support (XML, JSON, YAML)
- Binding context management
- Constraint validation during binding
IBindingContext context = IBindingContext.instance();
IDeserializer<T> reader = context.newDeserializer(Format.JSON, clazz);
ISerializer<T> writer = context.newSerializer(Format.JSON, clazz);
Schema generation:
- XML Schema (XSD) generation
- JSON Schema generation
XmlSchemaGenerator xsdGen = new XmlSchemaGenerator();
JsonSchemaGenerator jsonGen = new JsonSchemaGenerator();
Command-line tools:
- Module validation
- Schema generation
- Document validation
- Metapath evaluation
metaschema-cli validate module.xml
metaschema-cli generate-schema --as=xsd module.xml
metaschema-cli metapath eval -e "//control" document.json
Maven integration:
- Java class generation
- Schema generation
- Build integration
<plugin>
<groupId>dev.metaschema.java</groupId>
<artifactId>metaschema-maven-plugin</artifactId>
</plugin>
The Java packages follow a consistent naming convention that mirrors the module structure. This organization makes it easy to locate classes based on their functionality:
dev.metaschema
├── core
│ ├── model # Core model interfaces
│ ├── metapath # Metapath engine
│ └── constraint # Constraint definitions
├── databind
│ ├── io # Serialization/deserialization
│ └── model # Binding model
├── schemagen
│ ├── xml # XSD generation
│ └── json # JSON Schema generation
└── cli # Command-line tools
Understanding the core interfaces helps you navigate the codebase and work effectively with the API. This section describes the main abstractions and their relationships.
A Metaschema module represents a data model definition. The IModule interface provides access to all definitions within that module:
IModule
├── getName() # get the module's name
├── getXmlNamespace() # get the module's namespace used in XML
├── getAssemblyDefinitions() # get referenceable assembly definitions
├── getFieldDefinitions() # get referenceable field definitions
├── getFlagDefinitions() # get referenceable flag definitions
└── getImportedModules() # get other module's imported by this module
Metaschema defines three types of model elements. Assemblies are complex structures that contain other elements. Fields hold simple values but can have attributes (flags). Flags are the simplest elements, representing single attributes or properties:
IDefinition
├── IAssemblyDefinition # Complex structures
│ └── getModelInstances()
├── IFieldDefinition # Simple values with flags
│ └── getJavaTypeAdapter()
└── IFlagDefinition # Attributes/properties
└── isRequired()
The binding context is your main entry point for serialization operations. It knows about all registered model classes and creates serializers and deserializers on demand:
IBindingContext
├── newDeserializer(Format, Class)
├── newSerializer(Format, Class)
├── validate(Object)
├── getStaticContext() # For Metapath
└── registerModule(IModule)
Metapath expressions are compiled once and can be evaluated multiple times against different documents. The StaticContext provides namespace bindings and other configuration:
MetapathExpression
├── compile(String, StaticContext)
└── evaluate(Object) → ISequence<IItem>
StaticContext
├── builder()
├── namespace(prefix, uri)
└── baseUri(URI)
Understanding how data flows through the system helps you debug issues and optimize performance. This section traces data through the major operations.
When you load a Metaschema module, the system parses the XML/JSON/YAML resource, resolves any imports, and builds an in-memory model:
Metaschema Module XML/JSON/YAML
↓
MetaschemaLoader.load()
↓
XML/JSON/YAML Module Parsing
↓
IModule (in-memory model)
↓
Definition Resolution
↓
Constraint Processing
The Maven plugin generates Java source code from your module definitions. JavaPoet handles the actual code generation, producing clean, readable Java files:
IModule
↓
ClassGenerator
↓
JavaPoet
↓
.java Source Files
↓
Maven Compilation
↓
.class Files
When deserializing data, the system detects the format, parses it using the appropriate library (Jackson for JSON/YAML, StAX for XML), and maps the content to your generated model objects:
JSON/XML/YAML File
↓
Format Detection
↓
Parser (Jackson/StAX)
↓
Binding Layer
↓
Generated Model Objects
↓
Optional Constraint Validation
Metapath expressions are first compiled into an abstract syntax tree, then evaluated against a document to produce a sequence of results:
Expression String
↓
MetapathExpression.compile()
↓
Abstract Syntax Tree
↓
evaluate(document)
↓
ISequence<IItem>
This section covers topics important when building applications with the framework.
The framework provides several extension points for customization. These allow you to add domain-specific functionality without modifying the core libraries.
You can register custom Metapath functions to extend the expression language with domain-specific operations:
StaticContext context = StaticContext.builder()
.function(myFunction)
.build();
To customize how constraint violations are reported or handled, implement a custom validation handler:
IConstraintValidationHandler handler = new MyHandler();
deserializer.setConstraintValidationHandler(handler);
For data types not natively supported by Metaschema, you can create custom type adapters that handle serialization and deserialization:
// For custom data types
IJavaTypeAdapter<MyType> adapter = new MyTypeAdapter();
When using the library in multi-threaded applications, it's important to understand which components are thread-safe. In general, immutable objects and read-only operations are safe to share across threads, while serializers and deserializers should be created per-use:
| Component | Thread Safety |
|---|---|
IBindingContext |
Thread-safe (read) |
IDeserializer |
Not thread-safe (create per use) |
ISerializer |
Not thread-safe (create per use) |
IModule |
Immutable (thread-safe) |
MetapathExpression |
Immutable (thread-safe) |
The library has minimal external dependencies. At runtime, it relies on standard parsing libraries and logging infrastructure. Build-time dependencies are only needed when compiling from source or generating code.
-
Jackson (JSON/YAML processing)
-
StAX (XML processing)
-
SLF4J (logging)
-
SpotBugs annotations (null safety)
-
Maven
-
JavaPoet (code generation)
| Project | Relationship |
|---|---|
| liboscal-java | OSCAL bindings built on this |
| oscal-cli | CLI using OSCAL bindings |
| Metaschema | Metaschema specification |
Continue learning about the Metaschema Java Tools with these related guides:
- Loading Modules - Work with modules
- Generating Java Classes - Code generation
- Installation - Add to your project

