Fork me on GitHub

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);
        }
    }
}
  1. Validate early - Check documents as soon as they're loaded
  2. Log all findings - Even warnings may indicate issues
  3. Fail fast on errors - Don't process invalid documents
  4. Report locations - Include XPath/JSON Pointer for debugging
  5. 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: