invopop/jsonschema

repository·main·Indexed 21 days ago

https://github.com/invopop/jsonschema

A Go package that uses reflection to generate JSON Schemas (Draft 2020-12) from Go types. It is a feature-rich fork of alecthomas/jsonschema, optimized for modern Go versions (>= 1.24). It supports complex types, custom property fields via jsonschema and jsonschema_extras struct tags, and automatic description generation from Go comments. The package provides a Reflector for advanced configuration, including custom key naming, type mapping, and the ability to override schema logic via specific struct methods.

Tokens
7.1K
Snippets
21
Records
23
Agent score
75%

What's inside invopop/jsonschema

  1. Implement custom JSON schema logic via struct methods

    main

    You can control how specific types are represented in the generated schema by defining one of the following four methods on a non-pointer object. These methods allow you to override auto-generation, extend existing schemas, or provide aliases.

    • JSONSchema() *Schema: Prevents auto-generation and allows you to provide a completely custom schema definition.
    • JSONSchemaExtend(schema *jsonschema.Schema): Called after the schema is generated; use this to manipulate or add fields to the existing schema.
    • JSONSchemaAlias() any: Allows you to specify an alternative type to be used when reflecting this object.
    • JSONSchemaProperty(prop string) any: Called for every property inside a struct, allowing you to provide an alternative object for conversion into a schema.
    type CompactDate struct {
    	Year  int
    	Month int
    }
    
    // Custom schema definition using JSONSchema()
    func (CompactDate) JSONSchema() *Schema {
    	return &Schema{
    		Type:        "string",
    		Title:       "Compact Date",
    		Description: "Short date that only includes year and month",
    		Pattern:     "^[0-9]{4}-[0-1][0-9]$",
    	}
    }
  2. Generate JSON Schemas from Go types

    main

    Use the jsonschema.Reflect function to generate a JSON Schema from any Go type via reflection. The package supports complex types like interface{}, maps, and slices, and adheres to the JSON Schema Draft 2020-12 specification.

    Key Features:

    • Supports JSON Schema features: minLength, maxLength, pattern, format, etc.
    • Supports string and numeric enums.
    • Supports custom property fields via the jsonschema_extras struct tag.
    • Automatically adds unique Schema IDs based on the Go package URL (can be disabled using the Anonymous option).

    Requirements:

    • Go version >= 1.24 is required.
    import "github.com/invopop/jsonschema"
    
    type TestUser struct {
      ID   int    `json:"id"`
      Name string `json:"name"`
    }
    
    schema := jsonschema.Reflect(&TestUser{})
  3. Configure JSON Schema properties using `jsonschema` struct tags

    main

    You can control the generated JSON Schema properties by adding jsonschema tags to your Go struct fields. Supported keys include:

    • title: Sets the title of the property.
    • description: Sets the description of the property.
    • example: Provides one or more examples (e.g., example=joe,example=lucy).
    • default: Sets the default value.
    • oneof_required: Defines a oneOf constraint where specific fields are required (e.g., oneof_required=field_name).
    • oneof_type: Defines a oneOf constraint based on types (e.g., oneof_type=string;array).
    • enum: Defines a list of allowed values (e.g., enum=red,green,blue).
    type TestUser struct {
      Name      string `json:"name" jsonschema:"title=the name,description=The name of a friend,example=joe,example=lucy,default=alex"` 
      Metadata  interface{} `json:"metadata,omitempty" jsonschema:"oneof_type=string;array"` 
      FavColor  string `json:"fav_color,omitempty" jsonschema:"enum=red,green,enum=blue"` 
    }
  4. How to provide custom Schema definitions via interfaces

    main

    You can control how specific types are reflected by implementing one of the following interfaces on your Go types:

    1. Custom Schema Definition (customSchemaImpl)

    Implement JSONSchema() *Schema to provide a completely custom schema for a type. This is useful for types with custom JSON marshaling logic.

    2. Schema Extension (extendSchemaImpl)

    Implement JSONSchemaExtend(*Schema) to modify the generated schema after it has been created.

    3. Type Aliasing (aliasSchemaImpl)

    Implement JSONSchemaAlias() any to tell the reflector to use a different type's schema instead of the current type's schema.

    4. Property Aliasing (propertyAliasSchemaImpl)

    Implement JSONSchemaProperty(prop string) any to determine if a specific property should use a different type for its contents.

    type MyType struct{}
    
    // Using customSchemaImpl to provide a manual schema
    func (m MyType) JSONSchema() *jsonschema.Schema {
    	return &jsonschema.Schema{
    		Type: "string",
    		Format: "custom-format",
    	}
    }
  5. Configure ExpandedStruct in jsonschema.Reflector

    main

    When creating a jsonschema.Reflector instance, you can enable ExpandedStruct. If set to true, the top-level struct will not reference itself in the definitions (it will be rendered inline rather than via a $ref). Note that the type passed to Reflect must be a struct type.

    // Example of how ExpandedStruct affects output
    type SomeBaseType struct {
    	SomeBaseProperty int `json:"some_base_property"` 
    	// ...
    }
    
    // If ExpandedStruct is true, the top level struct is not a $ref to a definition.
  6. How Go comments are mapped to types and fields

    main

    When AddGoComments is called, it builds a CommentMap using fully qualified names as keys. This allows the Reflector to look up descriptions during schema generation.

    The key format used in the CommentMap is:

    • For Types: {package_path}.{Type_Name}
    • For Fields: {package_path}.{Type_Name}.{Field_Name}

    Example mapping:

    • Package: github.com/acme/api
    • Type: User
    • Field: Email
    • Key: github.com/acme/api.User.Email
  7. Add custom properties using `jsonschema_extras` struct tags

    main

    To add arbitrary or custom properties to the generated JSON Schema object that are not part of the standard JSON Schema specification, use the jsonschema_extras struct tag. The values provided in the tag are mapped directly to the property in the resulting schema.

    Example: jsonschema_extras:"a=b,foo=bar,foo=bar1" will result in a property a with value b, and a property foo with an array ["bar", "bar1"].

    type TestUser struct {
      Tags map[string]interface{} `json:"tags,omitempty" jsonschema_extras:"a=b,foo=bar,foo=bar1"` 
    }
  8. Customize JSON key naming with KeyNamer

    main

    If your JSON keys differ from your Go struct field names (e.g., using snake_case for APIs), you can provide a mapping function to the Reflector.KeyNamer option.

    If a field has an explicit json:"..." tag, the KeyNamer function will receive the value of that tag as the input string instead of the Go field name.

    import "github.com/stoewer/go-strcase"
    
    r := new(jsonschema.Reflector)
    r.KeyNamer = strcase.SnakeCase
    
    s := r.Reflect(&User{})
  9. Automatically generate schema descriptions from Go comments

    main

    You can use the AddGoComments(base, path string) method on a jsonschema.Reflector to parse your Go source files and automatically use existing Go comments as the description field in your JSON schema.

    To use this, provide the fully qualified Go module URL as the base argument and the directory path containing the source files as the path argument.

    r := new(Reflector)
    if err := r.AddGoComments("github.com/invopop/jsonschema", "./"); err != nil {
      // handle error
    }
    s := r.Reflect(&User{})
  10. Validate a Schema ID

    main

    Use the Validate() method on an ID to ensure it is a properly formatted URI. The validation checks that:

    • The ID is a valid URL.
    • It contains a hostname.
    • The hostname contains at least one dot (.).
    • It has a path.
    • The scheme is either http or https.
    err := myID.Validate()
    if err != nil {
        // handle invalid schema ID
    }
  11. Generate JSON Schema using ReflectFromType

    main

    Use ReflectFromType to generate a root schema directly from a reflect.Type using default settings.

    import (
    	"reflect"
    	"github.com/invopop/jsonschema"
    )
    
    schema := jsonschema.ReflectFromType(reflect.TypeOf(User{})