scheyaml

package module
v1.2.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 3, 2025 License: MIT Imports: 12 Imported by: 0

README

📅 Schema YAML

SchemaYAML processes a YAML file that is constrained by JSON schema by filling in default values and comments found in the schema. Additionally, the user input (the overrides) can be validated (or not with SkipValidate) to be compliant with the JSON schema or return an error if not the case.

The processing is configurable to restrict returning only the required properties, which can be useful when writing the user configuration to disk. This provides a minimal configuration example for the user while when processing the file all remaining defaults can be automatically filled in (by scheyaml). See Usage for an example.

ScheYAML returns either the textual representation (configurable with WithCommentMaxLength and WithIndent) or the raw *yaml.Node representation.

ScheYAML uses xeipuuv/gojsonschema's jsonschema.Schema as input.

⬇️ Installation

go get github.com/survivorbat/go-scheyaml

📋 Usage

A simple implementation assuming that a json-schema.json file is present is for example:

package main

import (
 _ "embed"
 "fmt"

 "github.com/kaptinlin/jsonschema"
 "github.com/survivorbat/go-scheyaml"
)

//go:embed json-schema.json
var file []byte

func main() {
 // load the jsonschema
 compiler := jsonschema.NewCompiler()
 schema, err := compiler.Compile(file)
 if err != nil {
  panic(err)
 }

 result, err := scheyaml.SchemaToYAML(schema, scheyaml.WithOverrideValues(map[string]any{
  "hello": "world",
 }))
 if err != nil {
  panic(err)
 }

 fmt.Println(result)
}

But using this ScheYAML can be especially useful when also generating the config structs based on the JSON schema using e.g. omissis/go-jsonschema:

go-jsonschema --capitalization=API --extra-imports json-schema.json --struct-name-from-title -o config.go -p config

Given some simple schema:

{
  "$schema": "http://json-schema.org/draft-04/schema#",
  "title": "Config",
  "type": "object",
  "properties": {
    "name": {
      "type": "string",
      "default": "Hello World"
    }
  }
}

Will generate the following (simplified) Go struct:

type Config struct {
 // Name corresponds to the JSON schema field "name".
 Name string `json:"name,omitempty" yaml:"name,omitempty" mapstructure:"name,omitempty"`
}

Given some config file that should be valid (an empty file):

# yaml-language-server: $schema=json-schema.json

Normally, the default values are "lost" when unmarshalling. That's where scheyaml can output a processed version according to the json schema of the input that can be read, in this case as if the user would have supplied:

# yaml-language-server: $schema=json-schema.json
name: Hello World

See the example tests in ./examples_test.go for more details.

Override- / Default Value Rules

When override values are supplied or the json schema contains default values, the following rules apply when determining which value to use:

  1. if the schema is nullable ("type": ["<type>", "null"]) and an override is specified for this key, use the override
  2. if the schema is not nullable and the override is not nil, use the override value
  3. if the schema has a default ("default": "abc") use the default value of the property
  4. if 1..N pattern properties match, use the first pattern property which has a default value (if any)

✅ Support

  • Feature to override values in output
  • Feature to override the comment on a missing default value
  • Basic types (string, number, integer, null)
  • Object type
  • Array
  • Refs
  • Pattern Properties
  • Add yaml server header
  • AnyOf
  • AllOf

🔭 Plans

Not much yet.

Documentation

Index

Examples

Constants

View Source
const NullValue = "null"

NullValue when setting an override to the 'null' value. This can be used as opposed to SkipValue where the key is omitted entirely

Variables

View Source
var ErrInvalidInput = errors.New("invalid input given")

ErrInvalidInput is returned if given parameters are invalid

View Source
var ErrParsing = errors.New("failed to parse/process jsonschema")

ErrParsing is returned if during evaluation of the schema an error occurs

View Source
var SkipValue skipValue = true

SkipValue can be set on any key to signal that it should be emitted from the result set

Functions

func SchemaToNode added in v0.0.1

func SchemaToNode(schema *jsonschema.Schema, opts ...Option) (*yaml.Node, error)

SchemaToNode is a lower-level version of SchemaToYAML, but returns the yaml.Node instead of the marshalled YAML.

You may provide options to customise the output.

func SchemaToYAML

func SchemaToYAML(schema *jsonschema.Schema, opts ...Option) ([]byte, error)

SchemaToYAML will take the given JSON schema and turn it into an example YAML file using fields like `description` and `examples` for documentation, `default` for default values and `properties` for listing blocks.

If any of the given properties match a regex given in pattern properties, the fields are added to the relevant fields.

You may provide options to customise the output.

Example
input := `{
    "type": "object",
    "properties": {
      "name": {
        "type": "string",
        "default": "Robin",
        "description": "The name of the customer"
      },
      "beverages": {
        "type": "array",
        "description": "A list of beverages the customer has consumed",
        "items": {
          "type": "object",
          "properties": {
            "name": {
              "type": "string", 
              "description": "The name of the beverage", 
              "examples": ["Coffee", "Tea", "Cappucino"]
            },
            "price": {
              "type": "number",
              "description": "The price of the product",
              "default": 4.5
            }
          }
        }
      }
    }
  }`

compiler := jsonschema.NewCompiler()
schema, _ := compiler.Compile([]byte(input))

result, _ := SchemaToYAML(schema)

fmt.Println(string(result))
Output:

# A list of beverages the customer has consumed
beverages:
    - # The name of the beverage
      #
      # Examples:
      # - Coffee
      # - Tea
      # - Cappucino
      name: null # TODO: Fill this in
      # The price of the product
      price: 4.5
# The name of the customer
name: Robin
Example (WithOverrideValues)
input := `{
    "type": "object",
    "properties": {
      "name": {
        "type": "string",
        "default": "Robin",
        "description": "The name of the customer"
      },
      "previous_orders": {
        "type": "array",
		"items": {
			"type": "string"
		},
        "description": "names of beverages the customer has consumed"
      },
      "beverages": {
        "type": "array",
        "description": "A list of beverages the customer has consumed",
        "items": {
          "type": "object",
          "properties": {
            "name": {
              "type": "string", 
              "description": "The name of the beverage", 
              "examples": ["Coffee", "Tea", "Cappucino"]
            },
            "price": {
              "type": "number",
              "description": "The price of the product",
              "default": 4.5
            },
            "description": {
              "type": "string"
            }
          }
        }
      }
    }
  }`

overrides := map[string]any{
	"name":            "John",
	"previous_orders": []string{"Water", "Tea"},
	"beverages": []any{
		map[string]any{"name": "Coffee"},
		map[string]any{"name": "Beer"},
	},
}

todoComment := "Do something with this"

compiler := jsonschema.NewCompiler()
schema, _ := compiler.Compile([]byte(input))

result, err := SchemaToYAML(schema, WithIndent(2), WithOverrideValues(overrides), WithTODOComment(todoComment))
if err != nil {
	fmt.Println(err)
}

fmt.Println(string(result))
Output:

# A list of beverages the customer has consumed
beverages:
  - description: null # Do something with this
    # The name of the beverage
    #
    # Examples:
    # - Coffee
    # - Tea
    # - Cappucino
    name: Coffee
    # The price of the product
    price: 4.5
  - description: null # Do something with this
    # The name of the beverage
    #
    # Examples:
    # - Coffee
    # - Tea
    # - Cappucino
    name: Beer
    # The price of the product
    price: 4.5
# The name of the customer
name: John
# names of beverages the customer has consumed
previous_orders:
  - Water
  - Tea

Types

type Config added in v0.0.2

type Config struct {
	// OutputHeader is added to the top of the generated result as a comment, This property is only available at the root level and not copied in
	// forProperty
	OutputHeader string

	// ValueOverride is a primitive value used outside of objects (mainly to support ItemsOverrides). For
	// example when the schema is an array of which the items are primitives (e.g. "string"), the overrides
	// are processed per item (hence the relation to ItemsOverrides) but are not of type map[string]any
	// that is commonly used in ValueOverrides.
	ValueOverride any

	// HasOverride is configured in conjunction with ValueOverride to distinguish between the explicit
	// and implicit nil
	HasOverride bool

	// ValueOverrides allows a user to override the default values of a schema with the given value(s).
	// Because a schema may nested, this takes the form of a map[string]any of which the structure must mimic
	// the schema to function.
	ValueOverrides map[string]any

	// ItemsOverrides allows a user to override the default values of a schema with the given value(s).
	// Because a schema may be a slice (of potentially nested maps) this is stored separately from ValueOverrides
	ItemsOverrides []any

	// PatternProperties inherited from parent
	PatternProperties []*jsonschema.Schema

	// TODOComment is used in case no default value was defined for a property. It is set by
	// default in NewConfig but can be emptied to remove the comment altogether.
	TODOComment string

	// OnlyRequired properties are returned
	OnlyRequired bool

	// LineLength prevents descriptions and unreasonably long lines. Can be disabled
	// completely by setting it to 0.
	LineLength uint

	// Indent used when marshalling to YAML. This property is only available at the root level and not copied in
	// forProperty
	Indent int

	// SkipValidate of the provided jsonschema and override values. Might result in undefined behavior, use
	// at own risk. This property is only available at the root level and not copied in forProperty
	SkipValidate bool
}

Config serves as the configuration object to allow customisation in the library

func NewConfig added in v0.0.2

func NewConfig() *Config

NewConfig instantiates a config object with default values

type InvalidSchemaError added in v1.1.0

type InvalidSchemaError struct {
	Errors map[string]*jsonschema.EvaluationError
}

InvalidSchemaError is returned when the schema is not valid, see jsonschema.Validate

func (InvalidSchemaError) Error added in v1.1.0

func (e InvalidSchemaError) Error() string

Error is a multiline string of the string->jsonschema.EvaluationError

type Option

type Option func(*Config)

Option is used to customise the output, we currently don't allow extensions yet

func OnlyRequired added in v0.0.5

func OnlyRequired() Option

OnlyRequired properties are returned

func SkipValidate added in v1.1.0

func SkipValidate() Option

SkipValidate will not evaluate jsonschema.Validate, might result in undefined behavior. Use at own risk

func WithCommentMaxLength added in v0.0.5

func WithCommentMaxLength(lineLength uint) Option

WithCommentMaxLength prevents descriptions generating unreasonably long lines. Can be disabled completely by setting it to 0.

func WithIndent added in v1.1.0

func WithIndent(indent int) Option

WithIndent amount of spaces to use when marshalling

func WithOverrideValues added in v0.0.2

func WithOverrideValues(values map[string]any) Option

WithOverrideValues allows you to override the default values from the JSON schema, you can nest map[string]any values to reach nested objects in the JSON schema.

func WithSchemaHeader added in v1.2.0

func WithSchemaHeader(schemaPath string) Option

WithSchemaHeader will add the `# yaml-language-server: $schema=[...]` header to the output, allowing IDEs to provide autocompletion.

func WithTODOComment added in v0.0.2

func WithTODOComment(comment string) Option

WithTODOComment allows you to set the 'TODO: Fill this in' comment in the output YAML. Can be set to an empty string to turn it off altogether

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL