Skip to content

Latest commit

 

History

History
424 lines (312 loc) · 10.9 KB

File metadata and controls

424 lines (312 loc) · 10.9 KB

API Reference

Complete reference for all methods and types in the JSON Schema library.

Compiler

NewCompiler() *Compiler

Creates a new schema compiler with default settings.

compiler := jsonschema.NewCompiler()

(*Compiler) Compile(schema []byte, id ...string) (*Schema, error)

Compiles a JSON schema from bytes. Optionally provide an ID for schema referencing.

// Compile schema without ID
schema, err := compiler.Compile([]byte(`{"type": "string"}`))

// Compile schema with specific ID for referencing
schema, err := compiler.Compile([]byte(`{"type": "object", ...}`), "user.json")

(*Compiler) RegisterFormat(name string, fn FormatFunc) *Compiler

Registers a custom format validator.

compiler.RegisterFormat("uuid", func(value string) bool {
    _, err := uuid.Parse(value)
    return err == nil
})

(*Compiler) UnregisterFormat(name string) *Compiler

Removes a previously registered custom format from the compiler. If AssertFormat is set to true, schemas that reference this format will fail validation; otherwise the format annotation will be ignored.

// Remove a custom format
compiler.UnregisterFormat("uuid")

(*Compiler) RegisterDefaultFunc(name string, fn DefaultFunc) *Compiler

Registers a function for dynamic default value generation.

// Register built-in function
compiler.RegisterDefaultFunc("now", jsonschema.DefaultNowFunc)

// Register custom function
compiler.RegisterDefaultFunc("uuid", func(args ...any) (any, error) {
    return uuid.New().String(), nil
})

// Use in schema
schema := `{
    "properties": {
        "id": {"default": "uuid()"},
        "timestamp": {"default": "now(2006-01-02)"}
    }
}`

Function signature: func(args ...any) (any, error)

  • Functions should handle argument parsing gracefully
  • Return values are used as defaults during unmarshaling
  • Errors cause fallback to literal string values

Schema

Validation Methods

(*Schema) Validate(data interface{}) *EvaluationResult

Validates data against the schema. Auto-detects input type.

result := schema.Validate(data)
if result.IsValid() {
    // Valid data
} else {
    // Handle errors
    for field, err := range result.Errors {
        fmt.Printf("%s: %s\n", field, err.Message)
    }
}

(*Schema) ValidateJSON(data []byte) *EvaluationResult

Optimized validation for JSON bytes.

result := schema.ValidateJSON([]byte(`{"name": "John"}`))

(*Schema) ValidateStruct(data interface{}) *EvaluationResult

Zero-copy validation for Go structs.

user := User{Name: "John", Age: 25}
result := schema.ValidateStruct(user)

(*Schema) ValidateMap(data map[string]interface{}) *EvaluationResult

Optimized validation for maps.

data := map[string]interface{}{"name": "John"}
result := schema.ValidateMap(data)

Unmarshal Methods

Important: Unmarshal methods do NOT perform validation. Always validate separately.

(*Schema) Unmarshal(dst, src interface{}) error

Unmarshals data into destination, applying default values from schema.

// Recommended workflow
result := schema.Validate(data)
if result.IsValid() {
    var user User
    err := schema.Unmarshal(&user, data)
    if err != nil {
        // Handle unmarshal error
    }
} else {
    // Handle validation errors
}

Supported source types:

  • []byte (JSON data)
  • map[string]interface{} (parsed JSON object)
  • Go structs and other types

Supported destination types:

  • *struct (Go struct pointer)
  • *map[string]interface{} (map pointer)
  • Other pointer types

Schema Configuration Methods

(*Schema) SetCompiler(compiler *Compiler) *Schema

Sets a custom compiler for the schema and returns the schema for method chaining.

// Create custom compiler with functions
compiler := jsonschema.NewCompiler()
compiler.RegisterDefaultFunc("now", jsonschema.DefaultNowFunc)
compiler.RegisterDefaultFunc("uuid", generateUUID)

// Set compiler on schema
schema := jsonschema.Object(
    jsonschema.Prop("id", jsonschema.String(jsonschema.Default("uuid()"))),
    jsonschema.Prop("timestamp", jsonschema.String(jsonschema.Default("now()"))),
).SetCompiler(compiler)

// Child schemas automatically inherit parent's compiler

(*Schema) Compiler() *Compiler

Returns the effective compiler for the schema with smart inheritance.

compiler := schema.Compiler()

// Inheritance order:
// 1. Current schema's compiler
// 2. Parent schema's compiler (recursive)
// 3. Default global compiler

Use cases:

  • Per-schema functions: Isolate function registries for different schemas
  • Function inheritance: Child schemas automatically use parent's compiler
  • Dynamic defaults: Enable function-based default value generation

Validation Results

*EvaluationResult

(*EvaluationResult) IsValid() bool

Returns true if validation passed.

if result.IsValid() {
    // Process valid data
}

(*EvaluationResult) Errors map[string]*EvaluationError

Map of validation errors by field path.

for field, err := range result.Errors {
    switch err.Keyword {
    case "required":
        fmt.Printf("Missing: %s\n", field)
    case "type":
        fmt.Printf("Wrong type: %s\n", field)
    default:
        fmt.Printf("%s: %s\n", field, err.Message)
    }
}

(*EvaluationResult) ToList(includeHierarchy ...bool) *List

Converts result to a flat list format.

list := result.ToList()
for field, message := range list.Errors {
    fmt.Printf("%s: %s\n", field, message)
}

(*EvaluationResult) ToLocalizedList(t Translator, includeHierarchy ...bool) *List

Converts result with localized error messages.

zh, _ := i18n.New("zh-Hans") // import "github.com/kaptinlin/jsonschema/i18n"
list := result.ToLocalizedList(zh)

(*EvaluationResult) DetailedErrors() map[string]string

Recommended for most users - Collects all detailed validation errors from the nested Details hierarchy. Returns a flattened map where keys are field paths and values are specific English error messages. This method helps access validation failures that might be buried in nested structures.

result := schema.Validate(data)
if !result.IsValid() {
    detailedErrors := result.DetailedErrors()
    for path, message := range detailedErrors {
        fmt.Printf("Field '%s': %s\n", path, message)
    }
}

(*EvaluationResult) LocalizedDetailedErrors(t Translator) map[string]string

Same as DetailedErrors, rendered through a Translator. Missing translations fall back to the built-in English message.

zh, _ := i18n.New("zh-Hans") // import "github.com/kaptinlin/jsonschema/i18n"
detailedErrors := result.LocalizedDetailedErrors(zh)
for path, message := range detailedErrors {
    fmt.Printf("字段 '%s': %s\n", path, message) // Chinese messages
}

Why use DetailedErrors instead of result.Errors?

  • result.Errors: Generic messages like "Property 'jobs' does not match the schema" ❌
  • DetailedErrors(): Specific messages like "Required property 'runs-on' is missing" ✅

Error Types

*EvaluationError

Validation error with detailed information.

Fields

  • Keyword string - JSON Schema keyword that failed
  • Code string - Error code for i18n
  • Message string - Human-readable error message
  • Params map[string]interface{} - Parameters for templating

(*EvaluationError) Localize(localizer *i18n.Localizer) string

Returns localized error message.

*UnmarshalError

Error during unmarshaling process.

Fields

  • Type string - Error category ("destination", "source", "defaults", "unmarshal")
  • Field string - Field that caused the error (if applicable)
  • Reason string - Human-readable reason
  • Err error - Wrapped underlying error
var unmarshalErr *jsonschema.UnmarshalError
if errors.As(err, &unmarshalErr) {
    switch unmarshalErr.Type {
    case "destination":
        // Invalid destination (nil, not pointer, etc.)
    case "source":
        // Invalid source data
    case "defaults":
        // Error applying default values
    case "unmarshal":
        // Error during unmarshaling
    }
}

Internationalization

The root package has zero translation-framework dependencies; pure validation users pay no Intl compile, link, or binary cost. Localization is opt-in via the Translator interface and the i18n subpackage.

Translator

The consumer-side interface the root package renders localized messages through. Implement it with any backend — a database, gettext, or a hardcoded map.

type Translator interface {
    Translate(code string, params map[string]any) (message string, ok bool)
}

Return ok=false when no translation exists; callers fall back to the built-in English message.

i18n.New(locale string) (jsonschema.Translator, error)

Creates a translator bound to one locale, backed by the built-in catalogs. Unknown locales are an error here rather than a silent fall back to English.

import "github.com/kaptinlin/jsonschema/i18n"

zh, err := i18n.New("zh-Hans")
if err != nil {
    log.Fatal(err)
}

Supported locales:

  • en - English
  • zh-Hans - Simplified Chinese
  • zh-Hant - Traditional Chinese
  • ja-JP - Japanese
  • ko-KR - Korean
  • fr-FR - French
  • de-DE - German
  • es-ES - Spanish
  • pt-BR - Portuguese (Brazil)

Common Patterns

Production Validation + Unmarshal

func ProcessData(schema *jsonschema.Schema, data []byte) (*User, error) {
    // Step 1: Validate
    result := schema.Validate(data)
    if !result.IsValid() {
        return nil, fmt.Errorf("validation failed: %v", result.Errors)
    }
    
    // Step 2: Unmarshal
    var user User
    if err := schema.Unmarshal(&user, data); err != nil {
        return nil, fmt.Errorf("unmarshal failed: %w", err)
    }
    
    return &user, nil
}

Conditional Processing

func ProcessWithWarnings(schema *jsonschema.Schema, data []byte) (*User, []string) {
    var warnings []string
    
    // Always unmarshal (applies defaults)
    var user User
    schema.Unmarshal(&user, data)
    
    // Check validation separately
    result := schema.Validate(data)
    if !result.IsValid() {
        for field, err := range result.Errors {
            warnings = append(warnings, fmt.Sprintf("%s: %s", field, err.Message))
        }
    }
    
    return &user, warnings
}

Localized Error Handling

func ValidateWithLocale(schema *jsonschema.Schema, data interface{}, locale string) error {
    result := schema.Validate(data)
    if result.IsValid() {
        return nil
    }
    
    translator, err := i18n.New(locale) // import "github.com/kaptinlin/jsonschema/i18n"
    if err != nil {
        return err
    }
    localizedList := result.ToLocalizedList(translator)
    
    var errors []string
    for field, message := range localizedList.Errors {
        errors = append(errors, fmt.Sprintf("%s: %s", field, message))
    }
    
    return fmt.Errorf("validation failed: %s", strings.Join(errors, "; "))
}