invopop / jsonschema Goto Github PK
View Code? Open in Web Editor NEWGenerate JSON Schemas from Go types
License: MIT License
Generate JSON Schemas from Go types
License: MIT License
I have a struct that defines base model:
type BaseParameters struct {
Hotel string `json:"hotel" jsonschema:"title=Hotel ID,description=Hotel ID"`
}
And a struct that defines client model, which is a subset of base model:
type Hotel string
var Hotels []interface{} = []interface{}{"15603"}
type Parameters struct {
BaseParameters
Hotel Hotel `json:"hotel"`
}
func (Hotel) JSONSchema() *jsonschema.Schema {
return &jsonschema.Schema{
Type: "string",
Title: "Hotel ID",
Description: "Hotel ID",
Enum: Hotels,
}
}
Json schema is:
{
"$schema": "http://json-schema.org/draft/2020-12/schema",
"$ref": "#/$defs/Parameters",
"$defs": {
"Hotel": {
"type": "string",
"enum": ["15603"],
"title": "Hotel ID",
"description": "Hotel ID"
},
"Parameters": {
"properties": {
"hotel": {
"$ref": "#/$defs/Hotel"
}
},
"additionalProperties": false,
"type": "object",
"required": [
"hotel",
"hotel"
]
}
}
}
Is it by design that required
contains two entries of hotel
?
Comments on types correctly have single newlines stripped from them, but comments on fields do not. This is because of the behavior described in the documentation for ExtractGoComments
:
When parsing type comments, we use the
go/doc
's Synopsis method to extract the first phrase only. Field comments, which tend to be much shorter, will include everything.
Synopsis is also the mechanism by which unmeaningful newlines are stripped, so we also lose this behavior for fields.
Related to (but not solved by) #67.
schema := jsonschema.Reflect((*struct {
T []struct {
Key string `json:"key"`
Value interface{} `json:"value"`
} `json:"table"`
})(nil))
produces (see on the Go playground):
{
"$schema": "http://json-schema.org/draft/2020-12/schema",
"$ref": "#/$defs/",
"$defs": {
"": {
"properties": {
"table": {
"$ref": "#/$defs/"
}
},
"additionalProperties": false,
"type": "object",
"required": [
"table"
]
}
}
}
The schema is recursive while the original Go type isn't. It looks like there is a problem in handling of anonymous types. Even more, when there is multiple anonymous types the result is completely corrupted.
For anonymous types I expect an inline schema, not a schema defined via a reference under #/$defs
.
Expected schema:
{
"$schema": "http://json-schema.org/draft/2020-12/schema",
"additionalProperties": false,
"properties": {
"table": {
"items": {
"additionalProperties": false,
"properties": {
"name": {
"type": "string"
},
"value": {
}
},
"required": [
"name",
"value"
],
"type": "object"
},
"type": "array",
}
},
"required": [
"table"
],
"type": "object"
}
Hi, I would like to know how to override/remove some jsonschema
property.
In the example below, the MAC
attribute is a slice of bytes and is represented as {"type":"string","contentEncoding":"base64"}
in the generated schema. However, in my case I made a custom marshaller so as to turn this slice into a string. So I would like to remove the contentEncoding
attribute in the schema.
I have tried to change its value but it is not reflected in the schema.
package main
import (
"encoding/json"
"fmt"
"net"
"github.com/invopop/jsonschema"
)
type NIC struct {
Name string `json:"name,omitempty"`
MAC net.HardwareAddr `json:"mac,omitempty" jsonschema:"contentEncoding=quoted-printable"`
}
func (nic *NIC) MarshalJSON() ([]byte, error) {
type Alias NIC
mac := ""
if nic.MAC != nil {
mac = nic.MAC.String()
}
return json.Marshal(&struct {
MAC string `json:"mac,omitempty"`
*Alias
}{
MAC: mac,
Alias: (*Alias)(nic),
})
}
func main() {
schema := jsonschema.Reflect(&NIC{})
data, _ := schema.MarshalJSON()
fmt.Println(string(data))
}
# output
{"$schema":"https://json-schema.org/draft/2020-12/schema","$ref":"#/$defs/NIC","$defs":{"HardwareAddr":{"type":"string","contentEncoding":"base64"},"NIC":{"properties":{"name":{"type":"string"},"mac":{"$ref":"#/$defs/HardwareAddr"}},"additionalProperties":false,"type":"object"}}}
Question. If I would like to Unmarshal my JSON schema bytes which contain custom attributes, should the Extras
field contain those attributes?
I tried that, but it looks empty now.
How should I build API schema when I have http methods, paths and json schema for requests and responses?
Right now I am thinking about map[HTTP_METHOD:HTTP_PATH]{Request: {}, Response{}}
but it does not feel right.
Also, is there a way to generate openapi from this, or some kind of web-ish documentation for technically less literate folk?
This is not a big issue but I noticed that on windows when constructing paths using path.join or related the comments
did not load. But if I replaced all the backslashes (normal microsoft) with slashes (the rest of the world) it works.
Slashes works
reflector.AddGoComments("github.com/lavoqualis/messages.go", "./sources" )
but backslash
reflector.AddGoComments("github.com/lavoqualis/messages.go", ".\\sources" )
doesn't.
Currently we have the ability to use "format" for non-array types. I would like to extend this functionality to arrays as well.
Example:
type Repo struct {
Branch string `yaml:"branch" json:"Branch,omitempty"`
URLs []string `yaml:"urls" json:"URLs" jsonschema:"type=array,format=uri"`
Names []string `yaml:"names" json:"Names"`
}
var jsonReflector jsonschema.Reflector
schema := jsonReflector.Reflect(&Repo{})
generates the following schema:
{
"$schema": "http://json-schema.org/draft/2020-12/schema",
"$ref": "#/$defs/Repo",
"$defs": {
"Repo": {
"properties": {
"Branch": {
"type": "string"
},
"URLs": {
"items": {
"type": "string"
},
"type": "array"
},
"Names": {
"items": {
"type": "string"
},
"type": "array"
}
},
"additionalProperties": false,
"type": "object",
"required": [
"URLs",
"Names"
]
}
}
}
Note that the "items" section for URLs does not include "format": "uri" in the items section, and therefore invalid URIs will not be caught when validating against this schema.
Is there any way to postprocess the generated schema for a type to add additional information given the corresponding StructField
object? E.g. to implement custom tags. There's jsonschema_extras
, but it's relatively limited in that it only really works with string or string array values. Reflector.Mapper
only passes the Type
, not the StructField
, and in any case it's somewhat tricky to use Mapper
to tell jsonschema to do its own thing and only modify the result (if you just call ReflectFromType
recursively, the definitions
map which is passed down the call chain in jsonschema doesn't get populated, which leads to weird behaviour elsewhere, plus you have to be careful so you don't accidentally recurse infinitely).
Hey, I've been trying to generate a schema for a custom slice type which is a field in a struct, but the default
values aren't computed
type MyString string
type MyStrings []MyString
type MyObject struct {
strs MyStrings `json:"my_strings" jsonschema:"description=This is my description,default=some,default=other"`
}
The MyStrings
definition is correct, with a schema of type array
, and items of type string
. In the struct schema, there is the property my_strings
, which points to the MyStrings
ref and with the CORRECT description. But there are no default values. I evaluated this by marshaling the resulting schema.
As soon as I use []string
instead of MyStrings
, the default values show up
I may try to fix it on my spare time, but does anyone have any clue on what might be going on?
As the title suggests, adding an = character in the regex seems to break the reflector. Using jsonschema_extras instead as a workaround is not ideal as it doesn't parse escaped commas.
Example:
Using this struct
type S struct{
Normal string `jsonschema:"pattern=$\\,=^"`
Extras string `jsonschema_extras:"pattern=$\\,=^"`
NoEquals string `jsonschema:"pattern=$\\,^"`
}
Generates this schema
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$ref": "#/$defs/S",
"$defs": {
"S": {
"properties": {
"Normal": {
"type": "string"
},
"Extras": {
"type": "string",
"": "^",
"pattern": "$\\"
},
"NoEquals": {
"type": "string",
"pattern": "$,^"
}
},
"additionalProperties": false,
"type": "object",
"required": [
"Normal",
"Extras",
"NoEquals"
]
}
}
}
I have noticed that if I put omit empty, it overrides the jsonschema:"required" tag. I feel that this should be the opposite.
Example
type Test struct {
Name string `json:"name,omitempty" jsonschema:"required"`
}
This will not be marked as required
When using json:",inline"
the generated schema still shows a separate nested object rather than inlining it.
See https://go.dev/play/p/zbh54GJfLsO.
type Params struct {
Name string
Settings *struct {
Foo string
Bar string
} `yaml:",inline" json:",inline"`
}
Is rendered like this
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$ref": "#/$defs/Params",
"$defs": {
"Params": {
"properties": {
"Name": {
"type": "string"
},
"Settings": {
"properties": {
"Foo": {
"type": "string"
},
"Bar": {
"type": "string"
}
},
"additionalProperties": false,
"type": "object",
"required": [
"Foo",
"Bar"
]
}
},
"additionalProperties": false,
"type": "object",
"required": [
"Name",
"Settings"
]
}
}
}
Instead of what I would expect i.e.
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$ref": "#/$defs/Params",
"$defs": {
"Params": {
"properties": {
"Name": {
"type": "string"
},
"Foo": {
"type": "string"
},
"Bar": {
"type": "string"
}
},
"additionalProperties": false,
"type": "object",
"required": [
"Name",
"Foo",
"Bar"
]
}
}
}
It would be nice to use oneof_ref
such that a field could be one of several already-defined objects, as suggested here:
https://stackoverflow.com/questions/57414600/json-schema-oneof-with-ref-to-wildcard-path
{
"$schema": "http://json-schema.org/draft-07/schema#",
"$defs": {
"ipv4": {...},
"ipv6": {...}
"ipAddress": {
"oneOf": [{
"$ref": "#/$defs/ipv4"
},{
"$ref": "#/$defs/ipv6"
}]
}
}
"properties": {
"anIPAddress": {
"$ref": "#/$defs/ipAddress"
}
}
}
and the Go code would look something like:
...
IPAddress any `json:"ipAddress" jsonschema:"oneof_ref=#/$defs/ipv4;#/$defs/ipv6"`
...
I'll try to put together a PR.
Is there a way to tell the tool to ignore some fields when creating the schema?
For example: jsonschema:"ignore"
the switch case in stringKeywords
in https://github.com/invopop/jsonschema/blob/main/reflect.go#L772 is missing a number of formats that are defined in the 2020-12 schema in https://json-schema.org/draft/2020-12/json-schema-validation#name-defined-formats
personally i only really care about date
, but i figure others might want other formats so i made this issue generic
func (Coustom) JSONSchema() *jsonschema.Schema { return &jsonschema.Schema{ Type:"string", } }
I just add this func to my coustom struct, and got a nil pointer from here:
github.com/invopop/jsonschema.(*Reflector).reflectCustomSchema(0x7f6855c136f8?, 0x7f687f7d8108?, {0xbe1478, 0xad3980})
/home/XXX/go/pkg/mod/github.com/invopop/[email protected]/reflect.go:423 +0xb0
github.com/invopop/jsonschema.(*Reflector).reflectTypeToSchema(0xc00021fa58, 0xc00021f960?, {0xbe1478, 0xad3980})
/home/XXX/go/pkg/mod/github.com/invopop/[email protected]/reflect.go:348 +0x89
github.com/invopop/jsonschema.(*Reflector).reflectTypeToSchemaWithID(0xc00021fa58, 0xbe1478?, {0xbe1478, 0xad3980})
/home/XXX/go/pkg/mod/github.com/invopop/[email protected]/reflect.go:324 +0x2c
github.com/invopop/jsonschema.(*Reflector).ReflectFromType(0xc00021fa58, {0xbe1478, 0xad3980})
/home/XXX/go/pkg/mod/github.com/invopop/[email protected]/reflect.go:244 +0xf3
github.com/invopop/jsonschema.(*Reflector).Reflect(0xb0?, {0xad3980?, 0xc0000c3340?})
/home/XXX/go/pkg/mod/github.com/invopop/[email protected]/reflect.go:230 +0x4a
I tried func (*Coustom) JSONSchema() *jsonschema.Schema
,but still got the same error.
Did I make a mistake somewhere?
Hello,
This library is really awesome but it's sometimes difficult to identify what changed between two versions.
It's seems that all changes done to this library are done via pullrequest so I think the current project would
highly benefit from https://github.com/release-drafter/release-drafter
Release drafter automatically release changelog based on pullrequest titles.
I saw from the examples in README that oneof_required
is supported however there was no mention of anyOf
. Looked through the source code and found that there's just one place in the code where anyOf
is defined but for oneof_required
there were a few lines that was parsing the oneof_required
into the parent. Code for that can be found here
My question is whether or not anyof_required
is supported in the same way that oneof_required
is supported. I tried generating the schema with anyof_required
and it didn't generate any anyOf
array however anyOf
is a valid part of JSON schema so it should be supported?
I use jsonschema_extras
to add my own schema attributes. But unfortunately, it does not work with JSON array values (don't confuse them with multiple values)
An example:
Some interface{} `json:"some,omitempty" jsonschema_extras:"requiredWhen=[1,2,3]"`
Returns me
..."some":{"requiredWhen":"[1"}},"additionalProperties":false....
I guess the problem is with a comma separator.
Should I try implementing JSONSchema
? Would it be possible with it?
Many thanks in advance!
Assuming we have two different packages HTTP
and TCP
, and they all have a Config
structure.
package http
type Config struct {
URL string `json:"url" jsonschema:"required,format=uri"`
Method string `json:"method" jsonschema:"required"`
}
package tcp
type Config struct {
Host string `json:"host" jsonschema:"required,format=hostname"`
Port int `json:"port" jsonschema:"required,minimum=1,maximum=65535"`
}
then, we have a struct involving these two packages
package server
type AllConfig struct {
TCP tcp.Config `json:"tcp" jsonschema:"omitempty"`
HTTP http.Config `json:"http" jsonschema:"omitempty"`
}
then we will have the same $ref = #/$defs/Config
for http
and tcp
in JSON Schema, but actually, the http.Config
is quite different with tcp.Config
, this causes the incorrect the JSON schema file.
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://github.com/invopop/jsonschema/test/all-config",
"$ref": "#/$defs/AllConfig",
"$defs": {
"AllConfig": {
"properties": {
"tcp": {
"$ref": "#/$defs/Config"
},
"http": {
"$ref": "#/$defs/Config"
}
},
"additionalProperties": false,
"type": "object",
"required": ["tcp", "http"]
},
"Config": {
"properties": {
"host": {
"type": "string",
"format": "hostname"
},
"port": {
"type": "integer",
"maximum": 65535,
"minimum": 1
}
},
"additionalProperties": false,
"type": "object",
"required": ["host", "port"]
}
}
}
So, is there a way we can define the $ref
name to make sure they are using different $defs
.
I've been trying to figure out why all of my struct fields are being marked as required until I found this in your code.
In effect, if I don't specify the JSON omitempty tag, it will mark all struct fields as required.
IMHO, the required condition should be set via your tags and not by omitempty. The omitempty tag prevents the JSON from outputting that field if it is not defined, but that doesn't necessarily mean that it is required or not. This also means that we need to mark every struct field as either omitempty or required.
Structs are currently rendered as kebab-case in the "$id"
field.
For example, TestUser
becomes:
"$id": "https://github.com/invopop/jsonschema/test-user"
When another field attempts to reference this struct, the Python3.10 "jschon" package resports this error:
jschon.exc.CatalogError: A source is not available for "https://github.com/invopop/jsonschema/TestUser"
but if the "$id"
line is changed from test-user
to TestUser
, then the "jschon" package parses the file successfully.
I will work on a PR for this issue.
I'm not knocking orderedmap but it doesn't enable inline definitions, which can be tedious if you want/need to create custom Schema
s as it breaks the flow of the definition.
Using
type Obj []struct {
Field string
Value *jsonschema.Schema
}
enables this sort of API:
func (CreateTenantRequest) JSONSchema() *jsonschema.Schema {
return &jsonschema.Schema{
Type: "object",
Properties: jsonschema.Obj{
{"name", &jsonschema.Schema{}},
},
}
}
instead of:
func (CreateTenantRequest) JSONSchema() *jsonschema.Schema {
p := orderedmap.New()
p.Set("name", &jsonschema.Schema{
Type: "string",
})
return &jsonschema.Schema{
Type: "object",
Properties: p,
}
}
Marshaling/Unmarshaling the JSON is incredibly straightforward with tidwall/sjson & tidwall/gjson:
https://go.dev/play/p/OxCa0Vyv6l4 (I used strings as the value to keep things simple / concise for demo purposes). Error checking and such was omitted as well.
Also, thanks for the package.
Hey everyone,
I tried to add an ID to a slice based schema type today but wasn't able to find out how to achieve this.
Minimal required steps are:
r := new(jsonschema.Reflector)
schema := r.Reflect(&[]struct{}{})
json, _ := schema.MarshalJSON()
println(string(json))
the output is:
{
"$schema":"https://json-schema.org/draft/2020-12/schema",
"items":{"properties":{},"additionalProperties":false,"type":"object"},
"type":"array"
}
Which options do I have to add an $id
field here?
Thanks in advance 🙇
if you use generics in some type for which schema is generated, in reference generated name will contain path of the generic argument. Which is an issue as in json schema references /
is used to identify the reference. I fixed it with this snippet:
r := new(jsonschema.Reflector)
r.Namer = func(t reflect.Type) string {
return strings.ReplaceAll(t.Name(), "/", "__")
}
Tho this is something that should probably be done by default.
Sometimes I find that when I have a custom MarshalJSON
and UnmarshalJSON
I am basically building a different structure and then returning json.Marshal(newType)
, so that my code looks like:
type SomeType struct {...}
type otherType struct {...}
func (s SomeType) MarshalJSON() ([]byte, error) {
var o *otherType = convertSomeTypeToOtherType(s)
return json.Marshal(o)
}
(a real-world example I'm dealing with right now is a map[string]string
that needs to be handed to a system that forbids additional properties, so MarshalJSON
converts it to a []struct{key:string, value:string}
and marshals that instead).
It would be great if I could also write:
func (s SomeType) JSONSchema() *jsonschema.Schema {
return jsonschema.Reflect(otherType{})
}
The returned schema from Reflect
has .Version
set and its own internal .Definitions
, though, and these don't get merged to the top, so I end up with an invalid final schema, with something like
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$ref": "#/$defs/SomeType",
"$defs": {
"SomeType": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$ref": "#/$defs/otherType",
"$defs": {
"otherType": {...}
}}}}
I can get around this by defining
func (s SomeType) JSONSchema() *jsonschema.Schema {
reflector := jsonschema.Reflector{
DoNotReference: true,
}
schema := reflector.Reflect(otherType{})
schema.Version = ""
return schema
}
but this seems clunky and forces me to fully expand otherType
without references.
A) Is there a better way to do this?
B) If not, is there any reason why reflectCustomSchema
couldn't check for definitions and a version on the returned schema, and extract and merge them into its own definitions if applicable?
This is example of desired schema:
// desired schema ("items": ... are close to "type: "array")
{
"$schema": "http://json-schema.org/draft/2020-12/schema",
"$ref": "#/$defs/Foo",
"$defs": {
"Foo": {
"properties": {
"foos": {
"oneOf": [
{
"items": {
"$ref": "#/$defs/Foo"
},
"type": "array"
},
{
"type": "null"
}
]
}
},
"additionalProperties": false,
"type": "object"
}
}
}
Bellow is actually a generated schema:
// generated by
// Foos []Foo `json:"foos,omitempty" jsonschema:"oneof_type=array;null,type=array"`
{
"$schema": "http://json-schema.org/draft/2020-12/schema",
"$ref": "#/$defs/Foo",
"$defs": {
"Foo": {
"properties": {
"foos": {
"oneOf": [
{
"type": "array"
},
{
"type": "null"
}
],
"items": {
"$ref": "#/$defs/Foo"
},
"type": "array"
}
},
"additionalProperties": false,
"type": "object"
}
}
}
Is it possible to get desired schema using invopop/jsonschema tags (annotations)?
Thanks
(This is only question. Feel free to close this issue after answering this question)
This is a fantastic library - thank you!
I'm very new to JSON schemas but am wondering if we can use Go interfaces to give more fine-grained control over JSON schemas to the underlying types represented, similar to how Go's MarshalJSON
and UnmarshalJSON
work. This would be an alternative mechanism to the existing jsonschema.Reflector.Mapper
function.
For example, consider the following type:
package mypackage
type CoinFlip string
const (
CoinFlipHeads CoinFlip = "heads"
CoinFlipTails CoinFlip = "tails"
)
Let's give it a JSONSchema
method:
func (CoinFlip) JSONSchema() *jsonschema.Schema {
return &jsonschema.Schema{
Type: "string",
Enum: []interface{}{
"heads",
"tails",
},
}
}
and define an interface in the jsonschema
package:
package jsonschema
type JSONSchema interface {
JSONSchema() *jsonschema.Schema
}
Now, when generating the JSON schema for a field, we can first check if the field's type implements in the JSONSchema
interface, and, if so, call its JSONSchema()
method directly. If the field's type does not implement the JSONSchema
interface then we can continue with the existing behavior.
Such an approach would effectively allows types to define their own schemas.
Does this make sense? I would be happy to help implement such a feature.
I am trying to use the generated schema of this library together with jsonschema2md to generate markdown documentation but I am running into an issue with nullable fields.
A field with the tag nullable
, as seen below
FailedAt *time.Time `json:"failed_at" jsonschema:"nullable,title=FailedAt,description=Optional timestamp from when this check result item failed"`
produces:
"failed_at": {
"oneOf": [
{
"type": "string",
"format": "date-time",
"title": "FailedAt",
"description": "Optional timestamp from when this check result item failed"
},
{
"type": "null"
}
]
Which is documented by jsonschema2md
as
`failed_at`
* is required
* Type: merged type ([Details](result-defs-result-properties-failed_at.md))
* cannot be null
* defined in: [Result](result-defs-result-properties-failed_at.md)
#### failed\_at Type
merged type ([Details](result-defs-result-properties-failed_at.md))
one (and only one) of
* [FailedAt](result-defs-result-properties-failed_at-oneof-failedat.md "check type definition")
* [Untitled null in Result](result-defs-result-properties-failed_at-oneof-1.md "check type definition")
As far as I can see, jsonschema2md
looks for "type": ["string", "null"]
to mark a field as nullable.
I don't know what's the correct way to do it.
Is there anyway to get this library to output "type": ["string", "null"]
instead of oneOf
?
Is there a documentation of the tags beside me having to go through the source code and guess what's doing what?
The omitempty
in below always remove tag jsonschema:minimum=0
.
Minimum int `json:"minimum,omitempty"`
What if i want add a tag minimum=0
for any number property in schema.MarshalJSON output, the Minimum
should chang to:
Minimum *int `json:"minimum,omitempty"`
In my application, I transform struct keys to snake_case before writing a JSON file.
For this, I use json-iterator's RegisterExtension
to rename keys to snake_case
But right now, it doesn't seem possible to reflect that using this package.
What I'd like is a new option for the Reflector
type, something like TransformKeys
, of type func(string) string
that gets run on every struct key before generating the schema.
This would be like a "key name" equivalent of Namer
, basically.
I might actually do a PR, it doesn't seem that complicated to implement.
this library seems to work fine with structs, but what about maps like bson.M{}
?
For example, this code:
package main
import (
"encoding/json"
"fmt"
"github.com/invopop/jsonschema"
"go.mongodb.org/mongo-driver/bson"
)
// Define your struct here. This should mirror your JSON data.
type MyData struct {
Name string `json:"name"`
Age int `json:"age"`
Address string `json:"address,omitempty"`
}
func main() {
// Generate a JSON schema for your struct
{
schema := jsonschema.Reflect(&MyData{})
// Marshal the schema into JSON
schemaJSON, err := json.MarshalIndent(schema, "", " ")
if err != nil {
panic(err)
}
// Print the JSON schema
fmt.Println(string(schemaJSON))
}
{
var MyData = bson.M{
"this_is_string": "value",
"this_is_int": 5,
"this_is_struct": &MyData{
Name: "<name?",
Age: 0,
Address: "<address>",
},
}
schema := jsonschema.Reflect(&MyData)
// Marshal the schema into JSON
schemaJSON, err := json.MarshalIndent(schema, "", " ")
if err != nil {
panic(err)
}
// Print the JSON schema
fmt.Println(string(schemaJSON))
}
}
yields this output:
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$ref": "#/$defs/MyData",
"$defs": {
"MyData": {
"properties": {
"name": {
"type": "string"
},
"age": {
"type": "integer"
},
"address": {
"type": "string"
}
},
"additionalProperties": false,
"type": "object",
"required": [
"name",
"age"
]
}
}
}
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://go.mongodb.org/mongo-driver/bson/primitive/m",
"$ref": "#/$defs/M",
"$defs": {
"M": {
"type": "object"
}
}
}
this schema def is not really correct (or useful at least):
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://go.mongodb.org/mongo-driver/bson/primitive/m",
"$ref": "#/$defs/M",
"$defs": {
"M": {
"type": "object"
}
}
}
The project I am working on includes structures that are a subset of json-schema itself. That is, a struct that can include an array or map of references to its own type.
With the following structs:
type Flat struct {
String string
Array []string
}
type Recursive struct {
String string
Array []*Recursive
}
Flat encodes fine, but Recursive results in a stack overflow.
A more complicated example of a struct that won't encode is the jsonschema.Schema struct itself, since it includes the following field
AllOf []*Schema `json:"allOf,omitempty"` // section 10.2.1.1
There doesn't seem to be a way to capture the fact that types may need references to IDs before the schemmas are fully generted.
Problem
When generating a schema, it would be beneficial to have the capability to set default values using an instance of the struct that is being converted into a schema.
Currently, this is challenging because the name specified in the struct tags may not correspond to the actual name of the struct field in the code, which makes it difficult to match the fields using their names.
Proposed Solution
To resolve this, we could include the original name of the struct field—unmodified by struct tags—in the schema. This would preserve the ability to match the fields based on their original names in the code, facilitating the setting of default values.
Alternatives Considered
An alternative method I have considered involves replacing the field name of the instantiated object with the name specified in the tag and using that for matching. However, this approach does not fully address the problem, as the name from the tag might be altered by a function like KeyNamer
.
Related: #68
I'll work on a PR.
The current Reflector
struct should be replaced with a set of option functions provided at run time. This a more idiomatic approach and results in an immutable configuration for each reflection invocation.
The objective is to end up with something like this:
s := jsonschema.Reflect(TestUser{}, jsonschema.WithBaseSchemaID("https://example.com/schemas"))
Shared options can be assigned to an array:
opts = []jsonschema.Option{
jsonschema.WithBaseSchemaID("https://example.com/schemas"),
jsonschema.AllowAdditionalProperties(),
}
s := jsonschema.Reflect(TestUser{}, opts...)
It would be nice to be able to preserve the newlines and full text in a "description" string (as "\n") so that other tools can take advantage of the formatting already performed in the original source.
I'm thinking that this option should be added as an optional flag at the end of the AddGoComments
method.
I'll try to put together a PR.
If we have the following type
type S struct {}
func (*S) JSONSchemaExtend(sc *jsonschema.Schema) {}
then the reflection will miss the fact that the type implements the method.
There's a workaround to defint the func having value receiver, but the pointer receiver should also work.
score.go
type Score struct {
Percent string `json:"percent,omitempty" jsonschema:"omitempty,pattern=^(0|[1-9][0-9]?)(([.])([0-9]{1,5}))?$,description=Percentage to achieve( 0>= anyscore < 100 example: 0, 10, 90, 99.9999)"`
....
....
....
}
result jsonschema
{
"$schema": "http://json-schema.org/draft-04/schema#",
"properties": {
"percent": {
"pattern": "^(0|[1-9][0-9]?)(.[0-9]{1",
"type": "string",
"description": "Percentage of SLO to achieve(100 or 0 or between 1 and 99)"
},
},
"additionalProperties": false,
"type": "object"
}
this can be resolved like ^(0|[1-9][0-9]?)(([.])([0-9]|[0-9]{2}|[0-9]{3}|[0-9]{4}|[0-9]{5}))?$
.. but need a better solution ... TIA
similar to #24
Example:
type Repo struct {
Foos []string `yaml:"foos" json:"URLs" jsonschema:"type=array,pattern=^.*[^*]$"`
}
When using default int value (0) for minimum
, maximum
, etc., the values are just skipped in the resulting schema.
It can be worked around with exclusiveMinimum=-1
, but the better way would be to make Schema.Minimum
field (as well as other related ones) a pointer.
https://json-schema.org/draft/2020-12/json-schema-validation.html#section-6.2.2
https://json-schema.org/draft/2020-12/json-schema-validation.html#section-6.2.4
Hello everyone !
I just came across a situation, where I wanted to add an anyof
attribute to my json-schema, but didn't want to overwrite all subschemas with a func (MyStruct) JSONSchema()
and then lose all the reflection on my struct tags.
So I thought we could expose more interface{}
to add specific part in the final json-schema, what do you think ?
I just did a simple test on my fork here: #49
I would be happy to discuss it with you !
I need to set "type": ["object", "null"] in my json schema, but haven't found a way to do this. Is there a way?
For now I use Reflect and then set .Type = "[object, null]" and then when the schema is done I do a replace all.. it works, but is a little hacky
because oneof_type
doesnt write to the Type
property of a Schema
object, the switch statement in structKeywordsFromTags
doesn't interpret any other tags. so a set of tags like
Field1 *string `jsonschema:"oneof_type=string;null,enum=VAL1,enum=VAL2"`
will not parse the enum tags, because oneof_type
sets the OneOf
property instead of the Type
property
in this particular example, its arguable that the enum values should not be parsed, but imo if null was an enum value as well i would say that they should be
I'm trying to mark a struct field as required, but only if another field is set.
It looks like this can be done with the dependentRequired
or dependentSchemas
keywords in the JSON Schema syntax.
I found these on this Stack Overflow question.
Can this be added to your library, or do you have a better way to do what I am attempting to accomplish?
Jotting down this idea... there are a lot of requests to be able to support more advanced definitions which start getting really complex inside Go tags. The current JSONSchema()
method helps support these cases, but has the tradeoff that you need to define the whole model.
A possible alternative could be to support an JSONSchemaExt(base *jsonschema.Schema)
method that will generate the base schema for the Type as much as it can, then allow the type to override and add any additional fields, e.g.:
func (User) JSONSchemaExt(base *jsonschema.Schema) {
base.AnyOf = &jsonschema.Schema{.....}
}
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.