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

format-validation.mddocs/

Format Validation

Built-in format attribute validation for common data formats and extensible format validation system. Format validation provides semantic validation beyond basic JSON Schema types, ensuring data conforms to specific formats like email addresses, URIs, dates, and custom formats.

Capabilities

FormatAttribute Interface

Base interface for implementing format validation logic.

/**
 * Interface for implementing custom format validation
 */
public interface FormatAttribute {
    /**
     * Get supported JSON node types for this format
     * @return EnumSet of NodeType values this format can validate
     */
    EnumSet<NodeType> supportedTypes();
    
    /**
     * Validate format against instance data
     * @param report ProcessingReport for collecting validation results
     * @param bundle MessageBundle for error messages
     * @param data FullData containing schema and instance information
     * @throws ProcessingException if format validation processing fails
     */
    void validate(ProcessingReport report, MessageBundle bundle, FullData data) throws ProcessingException;
}

/**
 * Abstract base class providing common functionality for format attributes
 */
public abstract class AbstractFormatAttribute implements FormatAttribute {
    /**
     * Constructor with format name and supported types
     * @param fmt Format name (e.g., "email", "date-time")
     * @param first Required supported type
     * @param other Additional supported types
     */
    protected AbstractFormatAttribute(String fmt, NodeType first, NodeType... other);
    
    /**
     * Get format name
     * @return String format name
     */
    protected final String getFormatName();
    
    /**
     * Get supported node types
     * @return EnumSet of supported NodeType values
     */
    public final EnumSet<NodeType> supportedTypes();
    
    /**
     * Create new processing message for this format
     * @param data Validation data
     * @param bundle Message bundle
     * @param key Message key
     * @return ProcessingMessage for error reporting
     */
    protected final ProcessingMessage newMsg(FullData data, MessageBundle bundle, String key);
}

Built-in Format Attributes

Common Format Attributes

/**
 * Email format validation (RFC 5322)
 */
public final class EmailAttribute extends AbstractFormatAttribute {
    public EmailAttribute();
}

/**
 * URI format validation (RFC 3986)
 */
public final class URIAttribute extends AbstractFormatAttribute {
    public URIAttribute();
}

/**
 * Date-time format validation (RFC 3339)
 */
public final class DateTimeAttribute extends AbstractFormatAttribute {
    public DateTimeAttribute();
}

/**
 * Hostname format validation (RFC 1034)
 */
public final class HostnameAttribute extends AbstractFormatAttribute {
    public HostnameAttribute();
}

/**
 * IPv4 address format validation
 */
public final class IPv4Attribute extends AbstractFormatAttribute {
    public IPv4Attribute();
}

/**
 * IPv6 address format validation (RFC 4291)
 */
public final class IPv6Attribute extends AbstractFormatAttribute {
    public IPv6Attribute();
}

/**
 * Regular expression format validation
 */
public final class RegexAttribute extends AbstractFormatAttribute {
    public RegexAttribute();
}

Draft v3 Specific Formats

/**
 * Date format validation (YYYY-MM-DD)
 */
public final class DateAttribute extends AbstractFormatAttribute {
    public DateAttribute();
}

/**
 * Time format validation (HH:MM:SS)
 */
public final class TimeAttribute extends AbstractFormatAttribute {
    public TimeAttribute();
}

/**
 * Phone number format validation
 */
public final class PhoneAttribute extends AbstractFormatAttribute {
    public PhoneAttribute();
}

/**
 * UTC milliseconds timestamp format validation
 */
public final class UTCMillisecAttribute extends AbstractFormatAttribute {
    public UTCMillisecAttribute();
}

Extra Format Attributes

/**
 * Base64 encoded string format validation
 */
public final class Base64Attribute extends AbstractFormatAttribute {
    public Base64Attribute();
}

/**
 * Hexadecimal string format validation
 */
public final class HexStringAttribute extends AbstractFormatAttribute {
    public HexStringAttribute();
}

/**
 * UUID format validation (RFC 4122)
 */
public final class UUIDAttribute extends AbstractFormatAttribute {
    public UUIDAttribute();
}

/**
 * MD5 hash format validation
 */
public final class MD5Attribute extends AbstractFormatAttribute {
    public MD5Attribute();
}

/**
 * SHA1 hash format validation
 */
public final class SHA1Attribute extends AbstractFormatAttribute {
    public SHA1Attribute();
}

/**
 * SHA256 hash format validation
 */
public final class SHA256Attribute extends AbstractFormatAttribute {
    public SHA256Attribute();
}

/**
 * SHA512 hash format validation
 */
public final class SHA512Attribute extends AbstractFormatAttribute {
    public SHA512Attribute();
}

Format Dictionaries

Pre-built collections of format attributes for different JSON Schema versions.

/**
 * Common format attributes dictionary
 */
public final class CommonFormatAttributesDictionary {
    /**
     * Get dictionary of common format attributes
     * @return Dictionary containing email, uri, datetime, hostname, ipv4, ipv6, regex formats
     */
    public static Dictionary<FormatAttribute> get();
}

/**
 * Draft v3 specific format attributes dictionary
 */
public final class DraftV3FormatAttributesDictionary {
    /**
     * Get dictionary of Draft v3 format attributes
     * @return Dictionary containing date, time, phone, utc-millisec formats
     */
    public static Dictionary<FormatAttribute> get();
}

/**
 * Draft v4 format attributes dictionary
 */
public final class DraftV4FormatAttributesDictionary {
    /**
     * Get dictionary of Draft v4 format attributes
     * @return Dictionary containing common formats for Draft v4
     */
    public static Dictionary<FormatAttribute> get();
}

/**
 * Extra format attributes dictionary
 */
public final class ExtraFormatsDictionary {
    /**
     * Get dictionary of additional format attributes
     * @return Dictionary containing base64, hex, uuid, md5, sha1, sha256, sha512 formats
     */
    public static Dictionary<FormatAttribute> get();
}

Usage Examples

Example 1: Using Built-in Format Validation

import com.github.fge.jsonschema.cfg.ValidationConfiguration;
import com.github.fge.jsonschema.main.JsonSchemaFactory;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;

// Enable format validation (disabled by default for performance)
ValidationConfiguration config = ValidationConfiguration.newBuilder()
    .setUseFormat(true)
    .freeze();

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

// Schema with format constraints
ObjectMapper mapper = new ObjectMapper();
JsonNode schema = mapper.readTree("{\n" +
    "  \"type\": \"object\",\n" +
    "  \"properties\": {\n" +
    "    \"email\": {\n" +
    "      \"type\": \"string\",\n" +
    "      \"format\": \"email\"\n" +
    "    },\n" +
    "    \"website\": {\n" +
    "      \"type\": \"string\",\n" +
    "      \"format\": \"uri\"\n" +
    "    },\n" +
    "    \"birthdate\": {\n" +
    "      \"type\": \"string\",\n" +
    "      \"format\": \"date-time\"\n" +
    "    },\n" +
    "    \"server\": {\n" +
    "      \"type\": \"string\",\n" +
    "      \"format\": \"hostname\"\n" +
    "    }\n" +
    "  }\n" +
    "}");

JsonSchema jsonSchema = factory.getJsonSchema(schema);

// Valid instance
JsonNode validInstance = mapper.readTree("{\n" +
    "  \"email\": \"user@example.com\",\n" +
    "  \"website\": \"https://example.com\",\n" +
    "  \"birthdate\": \"1990-01-15T08:30:00Z\",\n" +
    "  \"server\": \"api.example.com\"\n" +
    "}");

ProcessingReport validReport = jsonSchema.validate(validInstance);
System.out.println("Valid: " + validReport.isSuccess()); // true

// Invalid instance with format errors
JsonNode invalidInstance = mapper.readTree("{\n" +
    "  \"email\": \"invalid-email\",\n" +
    "  \"website\": \"not-a-uri\",\n" +
    "  \"birthdate\": \"invalid-date\",\n" +
    "  \"server\": \"invalid_hostname\"\n" +
    "}");

ProcessingReport invalidReport = jsonSchema.validate(invalidInstance);
if (!invalidReport.isSuccess()) {
    System.out.println("Format validation errors:");
    for (ProcessingMessage message : invalidReport) {
        System.out.println("  " + message.getMessage());
    }
}

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 ISBN numbers
 */
public final class ISBNFormatAttribute extends AbstractFormatAttribute {
    private static final String FORMAT_NAME = "isbn";
    
    public ISBNFormatAttribute() {
        super(FORMAT_NAME, NodeType.STRING);
    }
    
    @Override
    public void validate(ProcessingReport report, MessageBundle bundle, 
                        FullData data) throws ProcessingException {
        
        JsonNode instance = data.getInstance().getNode();
        String isbn = instance.asText();
        
        if (!isValidISBN(isbn)) {
            ProcessingMessage message = newMsg(data, bundle, "err.format.isbn")
                .putArgument("value", isbn);
            report.error(message);
        }
    }
    
    private boolean isValidISBN(String isbn) {
        // Remove hyphens and spaces
        String cleanISBN = isbn.replaceAll("[\\s-]", "");
        
        // Check ISBN-10 or ISBN-13
        return isValidISBN10(cleanISBN) || isValidISBN13(cleanISBN);
    }
    
    private boolean isValidISBN10(String isbn) {
        if (isbn.length() != 10) return false;
        
        int sum = 0;
        for (int i = 0; i < 9; i++) {
            if (!Character.isDigit(isbn.charAt(i))) return false;
            sum += (isbn.charAt(i) - '0') * (10 - i);
        }
        
        char checkChar = isbn.charAt(9);
        int checkDigit = (checkChar == 'X') ? 10 : Character.getNumericValue(checkChar);
        
        return (sum + checkDigit) % 11 == 0;
    }
    
    private boolean isValidISBN13(String isbn) {
        if (isbn.length() != 13) return false;
        
        int sum = 0;
        for (int i = 0; i < 12; i++) {
            if (!Character.isDigit(isbn.charAt(i))) return false;
            int digit = isbn.charAt(i) - '0';
            sum += (i % 2 == 0) ? digit : digit * 3;
        }
        
        int checkDigit = Character.getNumericValue(isbn.charAt(12));
        return (sum + checkDigit) % 10 == 0;
    }
}

// Register custom format attribute
Library customLibrary = DraftV4Library.get().thaw()
    .addFormatAttribute("isbn", new ISBNFormatAttribute())
    .freeze();

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

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

// Use custom format in schema
String schemaWithISBN = "{\n" +
    "  \"type\": \"object\",\n" +
    "  \"properties\": {\n" +
    "    \"book_isbn\": {\n" +
    "      \"type\": \"string\",\n" +
    "      \"format\": \"isbn\"\n" +
    "    }\n" +
    "  }\n" +
    "}";

Example 3: Format Validation Service

/**
 * Service for format validation with multiple format libraries
 */
public class FormatValidationService {
    private final JsonSchemaFactory standardFactory;
    private final JsonSchemaFactory extendedFactory;
    
    public FormatValidationService() {
        // Standard factory with built-in formats
        ValidationConfiguration standardConfig = ValidationConfiguration.newBuilder()
            .setUseFormat(true)
            .freeze();
        this.standardFactory = JsonSchemaFactory.newBuilder()
            .setValidationConfiguration(standardConfig)
            .freeze();
        
        // Extended factory with additional formats
        Library extendedLibrary = DraftV4Library.get().thaw()
            .addFormatAttribute("isbn", new ISBNFormatAttribute())
            .addFormatAttribute("credit-card", new CreditCardFormatAttribute())
            .addFormatAttribute("iban", new IBANFormatAttribute())
            .freeze();
        
        ValidationConfiguration extendedConfig = ValidationConfiguration.newBuilder()
            .addLibrary("http://json-schema.org/draft-04/schema#", extendedLibrary)
            .setUseFormat(true)
            .freeze();
        
        this.extendedFactory = JsonSchemaFactory.newBuilder()
            .setValidationConfiguration(extendedConfig)
            .freeze();
    }
    
    public ProcessingReport validateWithStandardFormats(JsonNode schema, JsonNode instance) throws ProcessingException {
        JsonSchema jsonSchema = standardFactory.getJsonSchema(schema);
        return jsonSchema.validate(instance);
    }
    
    public ProcessingReport validateWithExtendedFormats(JsonNode schema, JsonNode instance) throws ProcessingException {
        JsonSchema jsonSchema = extendedFactory.getJsonSchema(schema);
        return jsonSchema.validate(instance);
    }
    
    public List<String> getSupportedFormats(boolean includeExtended) {
        List<String> formats = Arrays.asList(
            "email", "uri", "date-time", "hostname", "ipv4", "ipv6", "regex"
        );
        
        if (includeExtended) {
            List<String> extended = new ArrayList<>(formats);
            extended.addAll(Arrays.asList("isbn", "credit-card", "iban", "base64", "uuid"));
            return extended;
        }
        
        return formats;
    }
}

Example 4: Conditional Format Validation

/**
 * Format validator with conditional validation logic
 */
public final class ConditionalEmailAttribute extends AbstractFormatAttribute {
    public ConditionalEmailAttribute() {
        super("conditional-email", NodeType.STRING);
    }
    
    @Override
    public void validate(ProcessingReport report, MessageBundle bundle, 
                        FullData data) throws ProcessingException {
        
        JsonNode instance = data.getInstance().getNode();
        JsonNode schema = data.getSchema().getNode();
        String email = instance.asText();
        
        // Check if strict validation is enabled in schema
        boolean strictMode = schema.path("strictEmail").asBoolean(false);
        
        if (strictMode) {
            validateStrictEmail(email, report, bundle, data);
        } else {
            validateBasicEmail(email, report, bundle, data);
        }
    }
    
    private void validateStrictEmail(String email, ProcessingReport report, 
                                   MessageBundle bundle, FullData data) throws ProcessingException {
        // Strict RFC 5322 validation
        if (!email.matches("^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$")) {
            ProcessingMessage message = newMsg(data, bundle, "err.format.email.strict")
                .putArgument("value", email);
            report.error(message);
        }
    }
    
    private void validateBasicEmail(String email, ProcessingReport report,
                                  MessageBundle bundle, FullData data) throws ProcessingException {
        // Basic email validation
        if (!email.contains("@") || !email.contains(".")) {
            ProcessingMessage message = newMsg(data, bundle, "err.format.email.basic")
                .putArgument("value", email);
            report.error(message);
        }
    }
}

// Usage in schema
String conditionalSchema = "{\n" +
    "  \"type\": \"object\",\n" +
    "  \"properties\": {\n" +
    "    \"email\": {\n" +
    "      \"type\": \"string\",\n" +
    "      \"format\": \"conditional-email\",\n" +
    "      \"strictEmail\": true\n" +
    "    }\n" +
    "  }\n" +
    "}";

Available Format Attributes

Common Formats (always available)

  • email - Email addresses (RFC 5322)
  • uri - Uniform Resource Identifiers (RFC 3986)
  • date-time - Date and time (RFC 3339)
  • hostname - Internet hostnames (RFC 1034)
  • ipv4 - IPv4 addresses
  • ipv6 - IPv6 addresses (RFC 4291)
  • regex - Regular expressions

Draft v3 Specific Formats

  • date - Date in YYYY-MM-DD format
  • time - Time in HH:MM:SS format
  • phone - Phone number validation
  • utc-millisec - UTC milliseconds timestamp

Extra Formats (available via ExtraFormatsDictionary)

  • base64 - Base64 encoded strings
  • hex - Hexadecimal strings
  • uuid - UUID strings (RFC 4122)
  • md5 - MD5 hash strings
  • sha1 - SHA1 hash strings
  • sha256 - SHA256 hash strings
  • sha512 - SHA512 hash strings

Best Practices

  1. Performance: Enable format validation only when needed (setUseFormat(true))
  2. Extensibility: Create custom formats for domain-specific validation requirements
  3. Error Messages: Provide clear, actionable error messages in custom formats
  4. Type Safety: Use appropriate NodeType constraints in format attributes
  5. Validation Logic: Keep format validation focused and efficient
  6. Testing: Thoroughly test custom formats with valid and invalid inputs
  7. Standards Compliance: Follow relevant RFCs and standards for format validation

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