Validating with Constraints
This guide explains how to validate OSCAL documents using Metaschema constraints.
Metaschema constraints provide validation beyond schema compliance:
- Cardinality - Required fields, maximum occurrences
- Allowed values - Enumerated value restrictions
- Patterns - Regex-based validation
- Uniqueness - Key constraints within scopes
- Cross-references - Reference integrity
- Custom rules - Metapath-based assertions
import dev.metaschema.databind.io.DeserializationFeature;
import dev.metaschema.databind.io.IBoundLoader;
import dev.metaschema.oscal.lib.OscalBindingContext;
import dev.metaschema.oscal.lib.model.Catalog;
import java.nio.file.Path;
OscalBindingContext context = OscalBindingContext.instance();
IBoundLoader loader = context.newBoundLoader();
// Enable constraint validation during loading
loader.enableFeature(DeserializationFeature.DESERIALIZE_VALIDATE_CONSTRAINTS);
Catalog catalog = loader.load(Catalog.class, Path.of("catalog.json"));
import dev.metaschema.core.model.validation.IValidationResult;
import dev.metaschema.oscal.lib.OscalBindingContext;
import java.net.URI;
import java.nio.file.Path;
OscalBindingContext context = OscalBindingContext.instance();
URI target = Path.of("catalog.json").toUri();
// Validate against constraints
IValidationResult result = context.validateWithConstraints(target, null);
if (!result.isPassing()) {
result.getFindings().forEach(finding -> {
System.err.println(finding.getSeverity() + ": " +
finding.getMessage());
});
}
import dev.metaschema.core.model.constraint.IConstraint.Level;
import dev.metaschema.core.model.validation.IValidationFinding;
import dev.metaschema.core.model.validation.IValidationResult;
import dev.metaschema.oscal.lib.OscalBindingContext;
import java.net.URI;
import java.nio.file.Path;
OscalBindingContext context = OscalBindingContext.instance();
URI target = Path.of("catalog.json").toUri();
IValidationResult result = context.validateWithConstraints(target, null);
// Check if validation passed
if (result.isPassing()) {
System.out.println("Validation passed");
} else {
System.out.println("Validation failed");
}
// Process findings
for (IValidationFinding finding : result.getFindings()) {
System.out.println(String.format("[%s] %s at %s",
finding.getSeverity(),
finding.getMessage(),
finding.getLocation()));
}
| Severity | Meaning |
|---|---|
CRITICAL |
Severe error, document unusable |
ERROR |
Constraint violation |
WARNING |
Potential issue, may be intentional |
INFORMATIONAL |
Note for awareness |
The framework provides FindingCollectingConstraintValidationHandler for collecting validation findings:
import dev.metaschema.core.model.constraint.FindingCollectingConstraintValidationHandler;
import dev.metaschema.core.model.constraint.IConstraint.Level;
// The handler implements IValidationResult
FindingCollectingConstraintValidationHandler handler =
new FindingCollectingConstraintValidationHandler();
// After validation completes, check results
if (!handler.isPassing()) {
handler.getFindings().forEach(finding -> {
System.err.println(finding.getSeverity() + ": " +
finding.getMessage());
});
}
// Check highest severity level
Level highestSeverity = handler.getHighestSeverity();
if (highestSeverity.ordinal() >= Level.ERROR.ordinal()) {
System.err.println("Validation failed with errors");
}
ERROR: Required field 'title' is missing at /catalog/metadata
Fix: Add the required metadata title:
{
"catalog": {
"metadata": {
"title": "My Catalog"
}
}
}
ERROR: Value 'not-a-uuid' is not a valid UUID at /catalog/uuid
Fix: Use a valid UUID v4 format.
ERROR: Reference 'control-999' not found at /profile/modify/set-parameters/0/param-id
Fix: Ensure referenced elements exist.
ERROR: Value 'unknown' is not allowed for property 'status' at /catalog/controls/0/props/0
Fix: Use allowed values defined by constraints.
import dev.metaschema.oscal.lib.model.SystemSecurityPlan;
IDeserializer<SystemSecurityPlan> deserializer = context.newDeserializer(
Format.JSON, SystemSecurityPlan.class);
deserializer.setConstraintValidationEnabled(true);
SystemSecurityPlan ssp = deserializer.deserialize(Path.of("ssp.json"));
import dev.metaschema.oscal.lib.model.Profile;
// Note: Profile validation includes checking import references
IDeserializer<Profile> deserializer = context.newDeserializer(
Format.JSON, Profile.class);
deserializer.setConstraintValidationEnabled(true);
Profile profile = deserializer.deserialize(Path.of("profile.json"));
public class OscalValidator {
public static void main(String[] args) {
if (args.length < 1) {
System.err.println("Usage: java OscalValidator <file>");
System.exit(2);
}
Path file = Path.of(args[0]);
OscalBindingContext context = OscalBindingContext.instance();
try {
IDeserializer<Catalog> deserializer = context.newDeserializer(
Format.valueOf(file), Catalog.class);
deserializer.setConstraintValidationEnabled(true);
Catalog catalog = deserializer.deserialize(file);
IValidationResult result = context.validate(catalog);
if (result.isPassing()) {
System.out.println("Validation passed");
System.exit(0);
} else {
result.getFindings().forEach(f ->
System.err.println(f.getSeverity() + ": " + f.getMessage()));
System.exit(1);
}
} catch (Exception e) {
System.err.println("Error: " + e.getMessage());
System.exit(2);
}
}
}
- Validate early - Check documents as soon as they're loaded
- Log all findings - Even warnings may indicate issues
- Fail fast on errors - Don't process invalid documents
- Report locations - Include XPath/JSON Pointer for debugging
- Consider severity - Some warnings may be acceptable
Note: Metaschema constraint validation is experimental. Some constraints may not be fully enforced. Schema-level validation (well-formedness, structure) is stable.
Continue learning about liboscal-java with these related guides:
- Reading & Writing Data - Load documents for validation
- Executing Metapath - Query validated content
- Architecture - Understand the validation pipeline

