CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/maven-com-github-fge--json-schema-validator

A comprehensive Java implementation of the JSON Schema validation specification supporting both draft v4 and v3 with complete validation capabilities and extensibility.

Pending
Overview
Eval results
Files

extensions.mddocs/

Custom Keywords and Extensions

Extensibility system for adding custom validation keywords, format attributes, and validation logic through the pluggable library system. The library architecture allows complete customization of validation behavior while maintaining compatibility with standard JSON Schema specifications.

Capabilities

Library System

Core library management for organizing keywords, digesters, validators, and format attributes.

/**
 * Immutable library containing validation components
 */
public final class Library {
    /**
     * Create new library builder
     * @return LibraryBuilder for customization
     */
    public static LibraryBuilder newBuilder();
    
    /**
     * Get syntax checkers for keyword syntax validation
     * @return Dictionary of keyword name to SyntaxChecker
     */
    public Dictionary<SyntaxChecker> getSyntaxCheckers();
    
    /**
     * Get digest processors for schema optimization
     * @return Dictionary of keyword name to Digester
     */
    public Dictionary<Digester> getDigesters();
    
    /**
     * Get validator constructors for keyword validation
     * @return Dictionary of keyword name to KeywordValidator constructor
     */
    public Dictionary<Constructor<? extends KeywordValidator>> getValidators();
    
    /**
     * Get format attributes for format validation
     * @return Dictionary of format name to FormatAttribute
     */
    public Dictionary<FormatAttribute> getFormatAttributes();
    
    /**
     * Create mutable copy for modification
     * @return LibraryBuilder with current library contents
     */
    public LibraryBuilder thaw();
}

/**
 * Builder for creating custom libraries
 */
public final class LibraryBuilder {
    /**
     * Add complete keyword definition to library
     * @param keyword Keyword instance with all components
     * @return This builder for method chaining
     */
    public LibraryBuilder addKeyword(Keyword keyword);
    
    /**
     * Remove keyword from library by name
     * @param name Keyword name to remove
     * @return This builder for method chaining
     */
    public LibraryBuilder removeKeyword(String name);
    
    /**
     * Add format attribute for format validation
     * @param name Format name (e.g., "email", "date-time")
     * @param attribute FormatAttribute implementation
     * @return This builder for method chaining
     */
    public LibraryBuilder addFormatAttribute(String name, FormatAttribute attribute);
    
    /**
     * Remove format attribute by name
     * @param name Format name to remove
     * @return This builder for method chaining
     */
    public LibraryBuilder removeFormatAttribute(String name);
    
    /**
     * Create immutable library instance
     * @return Library with configured components
     */
    public Library freeze();
}

Keyword Definition

Complete keyword definition including syntax checking, digesting, and validation.

/**
 * Immutable keyword definition with all validation components
 */
public final class Keyword {
    /**
     * Create new keyword builder with name
     * @param name Keyword name (e.g., "minLength", "pattern")
     * @return KeywordBuilder for configuration
     */
    public static KeywordBuilder newBuilder(String name);
    
    /**
     * Get keyword name
     * @return String name of this keyword
     */
    public String getName();
    
    /**
     * Create mutable copy for modification
     * @return KeywordBuilder with current keyword settings
     */
    public KeywordBuilder thaw();
}

/**
 * Builder for creating custom keywords
 */
public final class KeywordBuilder {
    /**
     * Set syntax checker for validating keyword syntax in schemas
     * @param syntaxChecker SyntaxChecker implementation
     * @return This builder for method chaining
     */
    public KeywordBuilder withSyntaxChecker(SyntaxChecker syntaxChecker);
    
    /**
     * Set custom digester for schema preprocessing
     * @param digester Digester implementation
     * @return This builder for method chaining
     */
    public KeywordBuilder withDigester(Digester digester);
    
    /**
     * Set identity digester for simple pass-through processing
     * @param first Required node type
     * @param other Additional supported node types
     * @return This builder for method chaining
     */
    public KeywordBuilder withIdentityDigester(NodeType first, NodeType... other);
    
    /**
     * Set simple digester for basic keyword extraction
     * @param first Required node type
     * @param other Additional supported node types
     * @return This builder for method chaining
     */
    public KeywordBuilder withSimpleDigester(NodeType first, NodeType... other);
    
    /**
     * Set validator class for keyword validation logic
     * @param c KeywordValidator implementation class
     * @return This builder for method chaining
     */
    public KeywordBuilder withValidatorClass(Class<? extends KeywordValidator> c);
    
    /**
     * Create immutable keyword instance
     * @return Keyword with configured components
     */
    public Keyword freeze();
}

Custom Keyword Validator Interface

Interface for implementing custom validation logic.

/**
 * Interface for implementing custom keyword validation logic
 */
public interface KeywordValidator {
    /**
     * Validate keyword against instance data
     * @param processor Validation processor for recursive validation
     * @param report Processing report for collecting validation results
     * @param bundle Message bundle for error messages
     * @param data Full validation data including schema and instance
     * @throws ProcessingException if validation processing fails
     */
    void validate(Processor<FullData, FullData> processor, ProcessingReport report, 
                 MessageBundle bundle, FullData data) throws ProcessingException;
}

/**
 * Abstract base class providing common functionality for keyword validators
 */
public abstract class AbstractKeywordValidator implements KeywordValidator {
    /**
     * Constructor with keyword name
     * @param keyword Name of the keyword this validator handles
     */
    protected AbstractKeywordValidator(String keyword);
    
    /**
     * Get keyword name
     * @return String name of handled keyword
     */
    protected final String getKeyword();
    
    /**
     * Validate keyword with automatic error reporting
     * @param processor Validation processor
     * @param report Processing report
     * @param bundle Message bundle
     * @param data Validation data
     * @throws ProcessingException if validation fails
     */
    public final void validate(Processor<FullData, FullData> processor, ProcessingReport report,
                              MessageBundle bundle, FullData data) throws ProcessingException;
    
    /**
     * Implement specific validation logic for this keyword
     * @param processor Validation processor
     * @param report Processing report
     * @param bundle Message bundle
     * @param data Validation data
     * @throws ProcessingException if validation fails
     */
    protected abstract void validate(Processor<FullData, FullData> processor, ProcessingReport report,
                                   MessageBundle bundle, FullData data) throws ProcessingException;
}

Digester Interface

Interface for preprocessing schemas before validation.

/**
 * Interface for implementing schema digesters
 */
public interface Digester {
    /**
     * Get supported JSON node types for this digester
     * @return EnumSet of supported NodeType values
     */
    EnumSet<NodeType> supportedTypes();
    
    /**
     * Digest schema into optimized form
     * @param schema JsonNode containing schema to digest
     * @return JsonNode with digested schema representation
     */
    JsonNode digest(JsonNode schema);
}

/**
 * Abstract base class for digesters
 */
public abstract class AbstractDigester implements Digester {
    /**
     * Constructor with keyword name and supported types
     * @param keyword Keyword name
     * @param first Required supported type
     * @param other Additional supported types
     */
    protected AbstractDigester(String keyword, NodeType first, NodeType... other);
    
    /**
     * Get supported node types
     * @return EnumSet of supported NodeType values
     */
    public final EnumSet<NodeType> supportedTypes();
    
    /**
     * Get keyword name
     * @return String keyword name
     */
    protected final String getKeyword();
}

Pre-built Libraries

Standard libraries for different JSON Schema versions.

/**
 * Pre-built library for JSON Schema Draft v3
 */
public final class DraftV3Library {
    /**
     * Get Draft v3 library instance
     * @return Library configured for JSON Schema Draft v3
     */
    public static Library get();
}

/**
 * Pre-built library for JSON Schema Draft v4
 */
public final class DraftV4Library {
    /**
     * Get Draft v4 library instance
     * @return Library configured for JSON Schema Draft v4
     */
    public static Library get();
}

/**
 * Pre-built library for JSON Schema Draft v4 HyperSchema
 */
public final class DraftV4HyperSchemaLibrary {
    /**
     * Get Draft v4 HyperSchema library instance
     * @return Library configured for JSON Schema Draft v4 HyperSchema
     */
    public static Library get();
}

Usage Examples

Example 1: Custom String Length Validator

import com.github.fge.jsonschema.keyword.validator.AbstractKeywordValidator;
import com.github.fge.jsonschema.core.report.ProcessingReport;
import com.github.fge.jsonschema.processors.data.FullData;

/**
 * Custom validator for exact string length requirement
 */
public final class ExactLengthValidator extends AbstractKeywordValidator {
    public ExactLengthValidator() {
        super("exactLength");
    }
    
    @Override
    protected void validate(Processor<FullData, FullData> processor, 
                          ProcessingReport report, MessageBundle bundle, 
                          FullData data) throws ProcessingException {
        
        JsonNode instance = data.getInstance().getNode();
        JsonNode schema = data.getSchema().getNode();
        
        if (!instance.isTextual()) {
            return; // Only validate strings
        }
        
        int expectedLength = schema.get("exactLength").asInt();
        int actualLength = instance.asText().length();
        
        if (actualLength != expectedLength) {
            ProcessingMessage message = newMsg(data, bundle, "err.exactLength")
                .putArgument("expected", expectedLength)
                .putArgument("actual", actualLength);
            report.error(message);
        }
    }
}

// Create and register the custom keyword
Keyword exactLengthKeyword = Keyword.newBuilder("exactLength")
    .withSyntaxChecker(new PositiveIntegerSyntaxChecker())
    .withSimpleDigester(NodeType.STRING)
    .withValidatorClass(ExactLengthValidator.class)
    .freeze();

// Add to custom library
Library customLibrary = DraftV4Library.get().thaw()
    .addKeyword(exactLengthKeyword)
    .freeze();

// Use with validation configuration
ValidationConfiguration config = ValidationConfiguration.newBuilder()
    .addLibrary("http://json-schema.org/draft-04/schema#", customLibrary)
    .freeze();

JsonSchemaFactory factory = JsonSchemaFactory.newBuilder()
    .setValidationConfiguration(config)
    .freeze();

Example 2: Custom Format Attribute

import com.github.fge.jsonschema.format.AbstractFormatAttribute;
import com.github.fge.jsonschema.core.report.ProcessingReport;
import com.github.fge.jsonschema.processors.data.FullData;

/**
 * Custom format attribute for validating credit card numbers
 */
public final class CreditCardFormatAttribute extends AbstractFormatAttribute {
    private static final String FORMAT_NAME = "credit-card";
    
    public CreditCardFormatAttribute() {
        super(FORMAT_NAME, NodeType.STRING);
    }
    
    @Override
    public void validate(ProcessingReport report, MessageBundle bundle, 
                        FullData data) throws ProcessingException {
        
        JsonNode instance = data.getInstance().getNode();
        String value = instance.asText();
        
        if (!isValidCreditCard(value)) {
            ProcessingMessage message = newMsg(data, bundle, "err.format.creditCard")
                .putArgument("value", value);
            report.error(message);
        }
    }
    
    private boolean isValidCreditCard(String number) {
        // Implement Luhn algorithm or other validation
        return number.matches("\\d{13,19}") && luhnCheck(number);
    }
    
    private boolean luhnCheck(String number) {
        // Luhn algorithm implementation
        int sum = 0;
        boolean alternate = false;
        for (int i = number.length() - 1; i >= 0; i--) {
            int n = Integer.parseInt(number.substring(i, i + 1));
            if (alternate) {
                n *= 2;
                if (n > 9) {
                    n = (n % 10) + 1;
                }
            }
            sum += n;
            alternate = !alternate;
        }
        return (sum % 10 == 0);
    }
}

// Register custom format attribute
Library customLibrary = DraftV4Library.get().thaw()
    .addFormatAttribute("credit-card", new CreditCardFormatAttribute())
    .freeze();

ValidationConfiguration config = ValidationConfiguration.newBuilder()
    .addLibrary("http://json-schema.org/draft-04/schema#", customLibrary)
    .setUseFormat(true)
    .freeze();

Example 3: Custom Digester

import com.github.fge.jsonschema.keyword.digest.AbstractDigester;

/**
 * Custom digester for optimizing range validation keywords
 */
public final class RangeDigester extends AbstractDigester {
    public RangeDigester() {
        super("range", NodeType.INTEGER, NodeType.NUMBER);
    }
    
    @Override
    public JsonNode digest(JsonNode schema) {
        ObjectNode digested = FACTORY.objectNode();
        JsonNode rangeNode = schema.get("range");
        
        if (rangeNode.isArray() && rangeNode.size() == 2) {
            digested.put("minimum", rangeNode.get(0));
            digested.put("maximum", rangeNode.get(1));
            digested.put("exclusiveMinimum", false);
            digested.put("exclusiveMaximum", false);
        }
        
        return digested;
    }
}

// Custom validator that works with digested schema
public final class RangeValidator extends AbstractKeywordValidator {
    public RangeValidator() {
        super("range");
    }
    
    @Override
    protected void validate(Processor<FullData, FullData> processor,
                          ProcessingReport report, MessageBundle bundle,
                          FullData data) throws ProcessingException {
        
        JsonNode instance = data.getInstance().getNode();
        JsonNode schema = data.getSchema().getNode();
        
        if (!instance.isNumber()) {
            return;
        }
        
        double value = instance.asDouble();
        double min = schema.get("minimum").asDouble();
        double max = schema.get("maximum").asDouble();
        
        if (value < min || value > max) {
            ProcessingMessage message = newMsg(data, bundle, "err.range")
                .putArgument("value", value)
                .putArgument("min", min)
                .putArgument("max", max);
            report.error(message);
        }
    }
}

// Create keyword with custom digester
Keyword rangeKeyword = Keyword.newBuilder("range")
    .withSyntaxChecker(new ArrayOfTwoNumbersSyntaxChecker())
    .withDigester(new RangeDigester())
    .withValidatorClass(RangeValidator.class)
    .freeze();

Example 4: Complete Custom Library

// Build comprehensive custom library
Library customLibrary = Library.newBuilder()
    // Add standard Draft v4 keywords
    .addKeyword(exactLengthKeyword)
    .addKeyword(rangeKeyword)
    
    // Add custom format attributes
    .addFormatAttribute("credit-card", new CreditCardFormatAttribute())
    .addFormatAttribute("phone-number", new PhoneNumberFormatAttribute())
    
    // Base on existing library and modify
    .freeze();

// Or extend existing library
Library extendedLibrary = DraftV4Library.get().thaw()
    .addKeyword(exactLengthKeyword)
    .addFormatAttribute("credit-card", new CreditCardFormatAttribute())
    .removeKeyword("deprecated-keyword")  // Remove unwanted keywords
    .freeze();

// Use in configuration
ValidationConfiguration config = ValidationConfiguration.newBuilder()
    .setDefaultLibrary("http://json-schema.org/draft-04/schema#", extendedLibrary)
    .setUseFormat(true)
    .freeze();

JsonSchemaFactory factory = JsonSchemaFactory.newBuilder()
    .setValidationConfiguration(config)
    .freeze();

// Schema can now use custom keywords
String schemaString = "{\n" +
    "  \"type\": \"object\",\n" +
    "  \"properties\": {\n" +
    "    \"username\": {\n" +
    "      \"type\": \"string\",\n" +
    "      \"exactLength\": 8\n" +
    "    },\n" +
    "    \"creditCard\": {\n" +
    "      \"type\": \"string\",\n" +
    "      \"format\": \"credit-card\"\n" +
    "    },\n" +
    "    \"score\": {\n" +
    "      \"type\": \"number\",\n" +
    "      \"range\": [0, 100]\n" +
    "    }\n" +
    "  }\n" +
    "}";

Extension Best Practices

  1. Validation Logic: Keep validators focused on single responsibility
  2. Error Messages: Provide clear, actionable error messages with relevant context
  3. Type Safety: Use appropriate NodeType constraints in digesters and validators
  4. Performance: Consider using digesters to optimize schema processing
  5. Reusability: Design keywords to be composable and reusable across schemas
  6. Testing: Thoroughly test custom extensions with various input scenarios
  7. Documentation: Document custom keywords and their expected schema syntax

Install with Tessl CLI

npx tessl i tessl/maven-com-github-fge--json-schema-validator

docs

basic-validation.md

cli.md

configuration.md

extensions.md

format-validation.md

index.md

syntax-validation.md

tile.json