Executing Metapath
This guide explains how to use Metapath expressions to query Metaschema-based data.
Metapath is an expression language for querying Metaschema-based documents. It's similar to XPath but works across all serialization formats (XML, JSON, YAML).
import dev.metaschema.core.metapath.MetapathExpression;
import dev.metaschema.core.metapath.IMetapathExpression;
import dev.metaschema.core.metapath.StaticContext;
import dev.metaschema.core.metapath.item.ISequence;
import dev.metaschema.core.metapath.item.IItem;
// Create static context
StaticContext staticContext = StaticContext.builder().build();
// Compile expression
IMetapathExpression expression = MetapathExpression.compile(
"//control", staticContext);
// Evaluate against a document
ISequence<?> results = expression.evaluate(document);
// Process results
for (IItem item : results) {
System.out.println("Found: " + item);
}
// Root element
"/catalog"
// All descendants named 'control'
"//control"
// Direct children
"catalog/group"
// Parent
".."
// Current node
"."
// By position
"//control[1]" // First control
"//control[last()]" // Last control
"//control[position() < 5]" // First four
// By attribute/flag
"//control[@id='ac-1']" // Control with id='ac-1'
// By child existence
"//control[title]" // Controls with title child
// Union
"//assembly | //field"
// Multiple predicates
"//control[@id='ac-1'][title]"
IMetapathExpression expr = MetapathExpression.compile(
"//control/@id", staticContext);
ISequence<?> results = expr.evaluate(document);
results.forEach(item -> {
String id = item.toAtomicItem().asString();
System.out.println("ID: " + id);
});
import dev.metaschema.databind.model.IBoundObject;
ISequence<?> results = expression.evaluate(document);
for (IItem item : results) {
if (item instanceof IBoundObject) {
Object value = ((IBoundObject) item).getValue();
// Work with the Java object
}
}
IMetapathExpression expr = MetapathExpression.compile(
"count(//control)", staticContext);
ISequence<?> result = expr.evaluate(document);
int count = result.getFirstItem(true)
.toAtomicItem()
.asInteger()
.intValue();
// String length
"string-length(//title)"
// Substring
"substring(//id, 1, 3)"
// String matching
"starts-with(@id, 'ac-')"
"ends-with(@id, '-1')"
"contains(title, 'Access')"
// Case conversion
"upper-case(title)"
"lower-case(title)"
// Concatenation
"concat(title, ' - ', @id)"
// Count
"count(//control)"
// Sum
"sum(//value)"
// Math
"ceiling(3.14)"
"floor(3.14)"
"round(3.14)"
"abs(-5)"
// Existence
"exists(//control)"
"empty(//withdrawn)"
// Logical
"not(//control)"
"true()"
"false()"
// First/last
"head(//control)"
"tail(//control)"
// Reverse
"reverse(//control)"
// Distinct values
"distinct-values(//prop/@name)"
StaticContext staticContext = StaticContext.builder().build();
StaticContext staticContext = StaticContext.builder()
.baseUri(URI.create("https://example.com/"))
.build();
StaticContext staticContext = StaticContext.builder()
.namespace("oscal", "http://csrc.nist.gov/ns/oscal/1.0")
.build();
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
public class MetapathCache {
private final Map<String, IMetapathExpression> cache =
new ConcurrentHashMap<>();
private final StaticContext staticContext;
public MetapathCache(StaticContext staticContext) {
this.staticContext = staticContext;
}
public IMetapathExpression get(String path) {
return cache.computeIfAbsent(path, p ->
MetapathExpression.compile(p, staticContext));
}
public ISequence<?> evaluate(Object document, String path) {
return get(path).evaluate(document);
}
}
import dev.metaschema.core.metapath.MetapathException;
try {
IMetapathExpression expr = MetapathExpression.compile(
"//control[invalid", staticContext);
} catch (MetapathException e) {
System.err.println("Invalid expression: " + e.getMessage());
}
// Find element with specific ID
"//control[@id='ac-1']"
// Find elements with any value for attribute
"//control[@id]"
// Find elements without attribute
"//control[not(@id)]"
// Children
"control/*"
// Specific child
"control/title"
// All descendants
"catalog//control"
// Parent's other children (siblings)
"../control"
// Elements containing text
"//control[contains(., 'security')]"
// Elements with specific child value
"//control[title = 'Access Control']"
// Count by type
"count(//control[prop[@name='type'][@value='technical']])"
// Get unique values
"distinct-values(//prop/@name)"
| Feature | Metapath | XPath |
|---|---|---|
| Format support | XML, JSON, YAML | XML only |
| Axis support | Simplified | Full |
| Node types | Metaschema types | XML nodes |
| Functions | Metaschema-specific | XPath functions |
- Compile once, use many - Cache compiled expressions
- Be specific - Narrow paths are more efficient
- Use predicates - Filter in expression, not in Java
- Handle empty results - Check sequence length
- Test expressions - Validate before embedding
Continue learning about the Metaschema Java Tools with these related guides:
- Validating with Constraints - Validate data
- Reading & Writing Data - Load documents
- Architecture - Understand Metapath internals

