Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion jsonschema/doc.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ or as a value of this type:

# Inference

The [For] and [ForType] functions return a [Schema] describing the given Go type.
The [For] function returns a [Schema] describing the given Go type.
The type cannot contain any function or channel types, and any map types must have a string key.
For example, calling For on the above Player type results in this schema:

Expand Down
49 changes: 24 additions & 25 deletions jsonschema/infer.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,40 +15,39 @@ import (

// For constructs a JSON schema object for the given type argument.
//
// It is a convenience for ForType.
func For[T any]() (*Schema, error) {
return ForType(reflect.TypeFor[T]())
}

// ForType constructs a JSON schema object for the given type.
// It translates Go types into compatible JSON schema types, as follows:
// - strings have schema type "string"
// - bools have schema type "boolean"
// - signed and unsigned integer types have schema type "integer"
// - floating point types have schema type "number"
// - slices and arrays have schema type "array", and a corresponding schema
// for items
// - maps with string key have schema type "object", and corresponding
// schema for additionalProperties
// - structs have schema type "object", and disallow additionalProperties.
// - Strings have schema type "string".
// - Bools have schema type "boolean".
// - Signed and unsigned integer types have schema type "integer".
// - Floating point types have schema type "number".
// - Slices and arrays have schema type "array", and a corresponding schema
// for items.
// - Maps with string key have schema type "object", and corresponding
// schema for additionalProperties.
// - Structs have schema type "object", and disallow additionalProperties.
// Their properties are derived from exported struct fields, using the
// struct field json name. Fields that are marked "omitempty" are
// struct field JSON name. Fields that are marked "omitempty" are
// considered optional; all other fields become required properties.
//
// It returns an error if t contains (possibly recursively) any of the following Go
// For returns an error if t contains (possibly recursively) any of the following Go
// types, as they are incompatible with the JSON schema spec.
// - maps with key other than 'string'
// - function types
// - complex numbers
// - unsafe pointers
//
// The cannot be any cycles in the types.
// TODO(rfindley): we could perhaps just skip these incompatible fields.
func ForType(t reflect.Type) (*Schema, error) {
return typeSchema(t)
// The types must not have cycles.
func For[T any]() (*Schema, error) {
// TODO: consider skipping incompatible fields, instead of failing.
s, err := forType(reflect.TypeFor[T]())
if err != nil {
var z T
return nil, fmt.Errorf("For[%T](): %w", z, err)
}
return s, nil
}

func typeSchema(t reflect.Type) (*Schema, error) {
func forType(t reflect.Type) (*Schema, error) {
// Follow pointers: the schema for *T is almost the same as for T, except that
// an explicit JSON "null" is allowed for the pointer.
allowNull := false
Expand Down Expand Up @@ -82,14 +81,14 @@ func typeSchema(t reflect.Type) (*Schema, error) {
return nil, fmt.Errorf("unsupported map key type %v", t.Key().Kind())
}
s.Type = "object"
s.AdditionalProperties, err = typeSchema(t.Elem())
s.AdditionalProperties, err = forType(t.Elem())
if err != nil {
return nil, fmt.Errorf("computing map value schema: %v", err)
}

case reflect.Slice, reflect.Array:
s.Type = "array"
s.Items, err = typeSchema(t.Elem())
s.Items, err = forType(t.Elem())
if err != nil {
return nil, fmt.Errorf("computing element schema: %v", err)
}
Expand All @@ -115,7 +114,7 @@ func typeSchema(t reflect.Type) (*Schema, error) {
if s.Properties == nil {
s.Properties = make(map[string]*Schema)
}
s.Properties[info.Name], err = typeSchema(field.Type)
s.Properties[info.Name], err = forType(field.Type)
if err != nil {
return nil, err
}
Expand Down
Loading