gojsonschema

repository·master·Indexed 25 days ago

https://github.com/xeipuuv/gojsonschema

A Go implementation of JSON Schema supporting drafts 04, 06, and 07. It provides functionality to validate JSON documents against schemas loaded from files, HTTP, JSON strings, or Go types. The library includes support for pre-compiling schemas, managing external references via SchemaLoader, meta-schema validation, and custom format checkers.

Tokens
5.9K
Snippets
12
Records
41
Agent score
83%

What's inside gojsonschema

  1. Add custom errors to validation results

    master

    To implement business-specific logic that goes beyond standard JSON Schema drafts, you can manually add errors to a gojsonschema.Result using the AddError method. This allows you to maintain a consistent error format within your application's result set.

    To do this, you typically create a custom error type that embeds gojsonschema.ResultErrorFields and use gojsonschema.NewJsonContext to define the location of the error.

    type AnswerInvalidError struct {
        gojsonschema.ResultErrorFields
    }
    
    func newAnswerInvalidError(context *gojsonschema.JsonContext, value interface{}, details gojsonschema.ErrorDetails) *AnswerInvalidError {
        err := AnswerInvalidError{}
        err.SetContext(context)
        err.SetType("custom_invalid_error")
        // it is important to use SetDescriptionFormat() as this is used to call SetDescription() after it has been parsed
        // using the description of err will be overridden by this.
        err.SetDescriptionFormat("Answer to the Ultimate Question of Life, the Universe, and Everything is {{.answer}}")
        err.SetValue(value)
        err.SetDetails(details)
    
        return &err
    }
    
    func main() {
        // ...
        schema, err := gojsonschema.NewSchema(schemaLoader)
        result, err := gojsonschema.Validate(schemaLoader, documentLoader)
    
        if true { // some validation
            jsonContext := gojsonschema.NewJsonContext("question", nil)
            errDetail := gojsonschema.ErrorDetails{
                "answer": 42,
            }
            result.AddError(
                newAnswerInvalidError(
                    gojsonschema.NewJsonContext("answer", jsonContext),
                    52,
                    errDetail,
                ),
                errDetail,
            )
        }
    
        return result, err
    }
  2. Load and manage external schema references

    master

    You can manually manage external schemas (referenced via $ref) by using a SchemaLoader. This allows you to pre-load schemas into memory so they don't need to be fetched via HTTP/File during compilation.

    1. Create a SchemaLoader with gojsonschema.NewSchemaLoader().
    2. Add individual schemas using sl.AddSchema(uri, loader).
    3. Add multiple schemas using sl.AddSchemas(loader) (useful if the loader contains a schema with an $id).
    4. Compile the main schema using sl.Compile(mainSchemaLoader). The compiled schema can now resolve its $ref pointers using the schemas previously added to the SchemaLoader.
    sl := gojsonschema.NewSchemaLoader()
    loader1 := gojsonschema.NewStringLoader(`{ "type" : "string" }`)
    err := sl.AddSchema("http://some_host.com/string.json", loader1)
    
    // Or if the loader has an $id:
    loader2 := gojsonschema.NewStringLoader(`{
        "$id" : "http://some_host.com/maxlength.json",
        "maxLength" : 5
    }`)
    err = sl.AddSchemas(loader2)
    
    // Compile the main schema which references the above
    loader3 := gojsonschema.NewStringLoader(`{
        "$id" : "http://some_host.com/main.json",
        "allOf" : [
            { "$ref" : "http://some_host.com/string.json" },
            { "$ref" : "http://some_host.com/maxlength.json" }
        ]
    }`)
    schema, err := sl.Compile(loader3)
  3. Configure JSON Schema drafts and autodetection

    master

    By default, gojsonschema attempts to detect the draft version (draft-04, draft-06, or draft-07) using the $schema keyword. If missing, it uses a hybrid mode. You can override this behavior using a SchemaLoader:

    • Set sl.AutoDetect = false to disable automatic detection.
    • Set sl.Draft = gojsonschema.Draft7 (or other supported drafts) to force a specific version.
    sl := gojsonschema.NewSchemaLoader()
    sl.Draft = gojsonschema.Draft7
    sl.AutoDetect = false
  4. Enable Meta-schema validation

    master

    To ensure your schemas themselves are valid according to the JSON Schema specification, enable meta-schema validation on your SchemaLoader by setting sl.Validate = true. This is particularly useful during schema development as it provides more descriptive errors.

    sl := gojsonschema.NewSchemaLoader()
    sl.Validate = true
    err := sl.AddSchemas(gojsonschema.NewStringLoader(`{
         "$id" : "http://some_host.com/invalid.json",
        "$schema": "http://json-schema.org/draft-07/schema#",
        "multipleOf" : true
    }`)) // This will error because multipleOf must be a number
  5. Use gojsonschema for basic validation

    master

    To validate a JSON document against a schema, use gojsonschema.Validate(schemaLoader, documentLoader). This is a quick way to perform a single validation without pre-compiling the schema.

    package main
    
    import (
        "fmt"
        "github.com/xeipuuv/gojsonschema"
    )
    
    func main() {
        schemaLoader := gojsonschema.NewReferenceLoader("file:///home/me/schema.json")
        documentLoader := gojsonschema.NewReferenceLoader("file:///home/me/document.json")
    
        result, err := gojsonschema.Validate(schemaLoader, documentLoader)
        if err != nil {
            panic(err.Error())
        }
    
        if result.Valid() {
            fmt.Printf("The document is valid\n")
        } else {
            fmt.Printf("The document is not valid. see errors :\n")
            for _, desc := range result.Errors() {
                fmt.Printf("- %s\n", desc)
            }
        }
    }
  6. Remove default format checkers

    master

    If you need to override or disable a built-in format validation, you can remove it from the global registry using gojsonschema.FormatCheckers.Remove("format_name").

    gojsonschema.FormatCheckers.Remove("hostname")
  7. Pre-compile schemas for multiple validations

    master

    If you need to validate multiple documents against the same schema, load the schema once using gojsonschema.NewSchema(schemaLoader) to create a Schema object. This is more efficient than calling Validate repeatedly.

    schema, err := gojsonschema.NewSchema(schemaLoader)
    ...
    result1, err := schema.Validate(documentLoader1)
    ...
    result2, err := schema.Validate(documentLoader2)
  8. Customize error messages with templates and functions

    master

    You can customize how error messages are rendered by providing custom template functions to gojsonschema.ErrorTemplateFuncs. These functions follow Go's text/template syntax and can be used within your localization templates.

    Example: Adding an allcaps function to capitalize field names in error messages.

    gojsonschema.ErrorTemplateFuncs = map[string]interface{}{
    	"allcaps": func(s string) string {
    		return strings.ToUpper(s)
    	},
    }
    
    // In a localization template, you can now use:
    // {{allcaps .field}} must be greater than or equal to {{.min}}
  9. Load JSON data with Loaders

    master

    Before validating, you must declare a loader to provide your schema and document data. The library supports several loading methods:

    • Web / HTTP: Use gojsonschema.NewReferenceLoader("http://...") for remote resources.
    • Local File: Use gojsonschema.NewReferenceLoader("file:///..."). Note that the file:// prefix and a full path are required.
    • JSON Strings: Use gojsonschema.NewStringLoader("{\"type\": \"string\"}") for raw JSON strings.
    • Custom Go Types: Use gojsonschema.NewGoLoader(data) where data can be a map[string]interface{} or a custom Go struct.
  10. Implement custom format checkers

    master

    You can extend gojsonschema by creating custom format checkers for repetitive or complex validation logic (e.g., checking a database or a specific string prefix).

    To implement a custom checker:

    1. Define a type that implements the gojsonschema.FormatChecker interface.
    2. Implement the IsFormat(input interface{}) bool method.
    3. Register the checker using gojsonschema.FormatCheckers.Add("format_name", checker).

    Important: When validating numbers, the input to IsFormat will be of type float64.

    // Define the format checker
    type RoleFormatChecker struct {}
    
    // Ensure it meets the gojsonschema.FormatChecker interface
    func (f RoleFormatChecker) IsFormat(input interface{}) bool {
        asString, ok := input.(string)
        if ok == false {
            return false
        }
    
        return strings.HasPrefix("ROLE_", asString)
    }
    
    // Add it to the library
    gojsonschema.FormatCheckers.Add("role", RoleFormatChecker{})
  11. Configure SchemaLoader options

    master

    The SchemaLoader struct provides several configuration fields:

    FieldTypeDescription
    AutoDetectboolIf true, the loader attempts to detect the JSON Schema draft version from the $schema field.
    ValidateboolIf true, the loader validates the loaded schema against its metaschema.
    DraftDraftSpecifies the JSON Schema draft version to use if AutoDetect is false or fails.
    pool*schemaPoolInternal cache of loaded schemas (not intended for direct manipulation).