From 7e5faab75cf2f7681684dd193c2ee5ad6af7838c Mon Sep 17 00:00:00 2001 From: Glenn Lewis <6598971+gmlewis@users.noreply.github.com> Date: Thu, 2 Feb 2023 09:30:16 -0500 Subject: [PATCH 1/5] Add support for anyof_ref Signed-off-by: Glenn Lewis <6598971+gmlewis@users.noreply.github.com> --- README.md | 1 + examples_test.go | 11 +++++++++++ reflect.go | 11 +++++++++++ 3 files changed, 23 insertions(+) diff --git a/README.md b/README.md index 100ad0c..cf4b9be 100644 --- a/README.md +++ b/README.md @@ -37,6 +37,7 @@ type TestUser struct { BirthDate time.Time `json:"birth_date,omitempty" jsonschema:"oneof_required=date"` YearOfBirth string `json:"year_of_birth,omitempty" jsonschema:"oneof_required=year"` Metadata interface{} `json:"metadata,omitempty" jsonschema:"oneof_type=string;array"` + IPAddress2 interface{} `json:"ipAddress2,omitempty" jsonschema:"anyof_ref=#/$defs/ipv4;#/$defs/ipv6"` FavColor string `json:"fav_color,omitempty" jsonschema:"enum=red,enum=green,enum=blue"` } ``` diff --git a/examples_test.go b/examples_test.go index 82cae05..87b1c58 100644 --- a/examples_test.go +++ b/examples_test.go @@ -16,6 +16,7 @@ type SampleUser struct { BirthDate time.Time `json:"birth_date,omitempty" jsonschema:"oneof_required=date"` YearOfBirth string `json:"year_of_birth,omitempty" jsonschema:"oneof_required=year"` Metadata interface{} `json:"metadata,omitempty" jsonschema:"oneof_type=string;array"` + IPAddress2 interface{} `json:"ipAddress2,omitempty" jsonschema:"anyof_ref=#/$defs/ipv4;#/$defs/ipv6"` FavColor string `json:"fav_color,omitempty" jsonschema:"enum=red,enum=green,enum=blue"` } @@ -93,6 +94,16 @@ func ExampleReflect() { // } // ] // }, + // "ipAddress2": { + // "anyOf": [ + // { + // "$ref": "#/$defs/ipv4" + // }, + // { + // "$ref": "#/$defs/ipv6" + // } + // ] + // }, // "fav_color": { // "type": "string", // "enum": [ diff --git a/reflect.go b/reflect.go index 6ebc6be..2c5ff23 100644 --- a/reflect.go +++ b/reflect.go @@ -720,6 +720,17 @@ func (t *Schema) genericKeywords(tags []string, parent *Schema, propertyName str parent.AnyOf = append(parent.AnyOf, typeFound) } typeFound.Required = append(typeFound.Required, propertyName) + case "anyof_ref": + if t.AnyOf == nil { + t.AnyOf = make([]*Schema, 0, 1) + } + t.Ref = "" + refs := strings.Split(nameValue[1], ";") + for _, r := range refs { + t.AnyOf = append(t.AnyOf, &Schema{ + Ref: r, + }) + } case "oneof_type": if t.OneOf == nil { t.OneOf = make([]*Schema, 0, 1) From ac7610f83b0ca37445186da0628694ec5a1b642d Mon Sep 17 00:00:00 2001 From: Glenn Lewis <6598971+gmlewis@users.noreply.github.com> Date: Thu, 2 Feb 2023 09:43:30 -0500 Subject: [PATCH 2/5] Add slice of anyof_ref Signed-off-by: Glenn Lewis <6598971+gmlewis@users.noreply.github.com> --- README.md | 1 + examples_test.go | 32 +++++++++++++++++++++++--------- reflect.go | 12 ++++++++---- 3 files changed, 32 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index cf4b9be..b9e45b1 100644 --- a/README.md +++ b/README.md @@ -38,6 +38,7 @@ type TestUser struct { YearOfBirth string `json:"year_of_birth,omitempty" jsonschema:"oneof_required=year"` Metadata interface{} `json:"metadata,omitempty" jsonschema:"oneof_type=string;array"` IPAddress2 interface{} `json:"ipAddress2,omitempty" jsonschema:"anyof_ref=#/$defs/ipv4;#/$defs/ipv6"` + IPAddresses2 []interface{} `json:"ipAddresses2,omitempty" jsonschema:"anyof_ref=#/$defs/ipv4;#/$defs/ipv6"` FavColor string `json:"fav_color,omitempty" jsonschema:"enum=red,enum=green,enum=blue"` } ``` diff --git a/examples_test.go b/examples_test.go index 87b1c58..7fb83c3 100644 --- a/examples_test.go +++ b/examples_test.go @@ -9,15 +9,16 @@ import ( ) type SampleUser struct { - ID int `json:"id"` - Name string `json:"name" jsonschema:"title=the name,description=The name of a friend,example=joe,example=lucy,default=alex"` - Friends []int `json:"friends,omitempty" jsonschema_description:"The list of IDs, omitted when empty"` - Tags map[string]interface{} `json:"tags,omitempty" jsonschema_extras:"a=b,foo=bar,foo=bar1"` - BirthDate time.Time `json:"birth_date,omitempty" jsonschema:"oneof_required=date"` - YearOfBirth string `json:"year_of_birth,omitempty" jsonschema:"oneof_required=year"` - Metadata interface{} `json:"metadata,omitempty" jsonschema:"oneof_type=string;array"` - IPAddress2 interface{} `json:"ipAddress2,omitempty" jsonschema:"anyof_ref=#/$defs/ipv4;#/$defs/ipv6"` - FavColor string `json:"fav_color,omitempty" jsonschema:"enum=red,enum=green,enum=blue"` + ID int `json:"id"` + Name string `json:"name" jsonschema:"title=the name,description=The name of a friend,example=joe,example=lucy,default=alex"` + Friends []int `json:"friends,omitempty" jsonschema_description:"The list of IDs, omitted when empty"` + Tags map[string]interface{} `json:"tags,omitempty" jsonschema_extras:"a=b,foo=bar,foo=bar1"` + BirthDate time.Time `json:"birth_date,omitempty" jsonschema:"oneof_required=date"` + YearOfBirth string `json:"year_of_birth,omitempty" jsonschema:"oneof_required=year"` + Metadata interface{} `json:"metadata,omitempty" jsonschema:"oneof_type=string;array"` + IPAddress2 interface{} `json:"ipAddress2,omitempty" jsonschema:"anyof_ref=#/$defs/ipv4;#/$defs/ipv6"` + IPAddresses2 []interface{} `json:"ipAddresses2,omitempty" jsonschema:"anyof_ref=#/$defs/ipv4;#/$defs/ipv6"` + FavColor string `json:"fav_color,omitempty" jsonschema:"enum=red,enum=green,enum=blue"` } func ExampleReflect() { @@ -104,6 +105,19 @@ func ExampleReflect() { // } // ] // }, + // "ipAddresses2": { + // "items": { + // "anyOf": [ + // { + // "$ref": "#/$defs/ipv4" + // }, + // { + // "$ref": "#/$defs/ipv6" + // } + // ] + // }, + // "type": "array" + // }, // "fav_color": { // "type": "string", // "enum": [ diff --git a/reflect.go b/reflect.go index 2c5ff23..1796fa6 100644 --- a/reflect.go +++ b/reflect.go @@ -721,13 +721,17 @@ func (t *Schema) genericKeywords(tags []string, parent *Schema, propertyName str } typeFound.Required = append(typeFound.Required, propertyName) case "anyof_ref": - if t.AnyOf == nil { - t.AnyOf = make([]*Schema, 0, 1) + subSchema := t + if t.Items != nil { + subSchema = t.Items } - t.Ref = "" + if subSchema.AnyOf == nil { + subSchema.AnyOf = make([]*Schema, 0, 1) + } + subSchema.Ref = "" refs := strings.Split(nameValue[1], ";") for _, r := range refs { - t.AnyOf = append(t.AnyOf, &Schema{ + subSchema.AnyOf = append(subSchema.AnyOf, &Schema{ Ref: r, }) } From cbbe2c13a6b9becf8e493a181023d9284e09ed56 Mon Sep 17 00:00:00 2001 From: Glenn Lewis <6598971+gmlewis@users.noreply.github.com> Date: Thu, 2 Feb 2023 09:58:12 -0500 Subject: [PATCH 3/5] Update README.md Signed-off-by: Glenn Lewis <6598971+gmlewis@users.noreply.github.com> --- README.md | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index b9e45b1..2c69510 100644 --- a/README.md +++ b/README.md @@ -51,7 +51,8 @@ jsonschema.Reflect(&TestUser{}) ```json { - "$schema": "http://json-schema.org/draft/2020-12/schema", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://github.com/invopop/jsonschema_test/sample-user", "$ref": "#/$defs/SampleUser", "$defs": { "SampleUser": { @@ -105,6 +106,29 @@ jsonschema.Reflect(&TestUser{}) } ] }, + "ipAddress2": { + "anyOf": [ + { + "$ref": "#/$defs/ipv4" + }, + { + "$ref": "#/$defs/ipv6" + } + ] + }, + "ipAddresses2": { + "items": { + "anyOf": [ + { + "$ref": "#/$defs/ipv4" + }, + { + "$ref": "#/$defs/ipv6" + } + ] + }, + "type": "array" + }, "fav_color": { "type": "string", "enum": ["red", "green", "blue"] From c56dbde396f4f411b885c9d0c11f0483c15e05f4 Mon Sep 17 00:00:00 2001 From: Glenn Lewis <6598971+gmlewis@users.noreply.github.com> Date: Mon, 26 Jun 2023 20:09:46 -0400 Subject: [PATCH 4/5] Address review feedback Signed-off-by: Glenn Lewis <6598971+gmlewis@users.noreply.github.com> --- README.md | 8 ++++---- examples_test.go | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 2c69510..4510cd2 100644 --- a/README.md +++ b/README.md @@ -37,8 +37,8 @@ type TestUser struct { BirthDate time.Time `json:"birth_date,omitempty" jsonschema:"oneof_required=date"` YearOfBirth string `json:"year_of_birth,omitempty" jsonschema:"oneof_required=year"` Metadata interface{} `json:"metadata,omitempty" jsonschema:"oneof_type=string;array"` - IPAddress2 interface{} `json:"ipAddress2,omitempty" jsonschema:"anyof_ref=#/$defs/ipv4;#/$defs/ipv6"` - IPAddresses2 []interface{} `json:"ipAddresses2,omitempty" jsonschema:"anyof_ref=#/$defs/ipv4;#/$defs/ipv6"` + IPAddress2 interface{} `json:"ip_address2,omitempty" jsonschema:"anyof_ref=#/$defs/ipv4;#/$defs/ipv6"` + IPAddresses2 []interface{} `json:"ip_addresses2,omitempty" jsonschema:"anyof_ref=#/$defs/ipv4;#/$defs/ipv6"` FavColor string `json:"fav_color,omitempty" jsonschema:"enum=red,enum=green,enum=blue"` } ``` @@ -106,7 +106,7 @@ jsonschema.Reflect(&TestUser{}) } ] }, - "ipAddress2": { + "ip_address2": { "anyOf": [ { "$ref": "#/$defs/ipv4" @@ -116,7 +116,7 @@ jsonschema.Reflect(&TestUser{}) } ] }, - "ipAddresses2": { + "ip_addresses2": { "items": { "anyOf": [ { diff --git a/examples_test.go b/examples_test.go index 7fb83c3..603becc 100644 --- a/examples_test.go +++ b/examples_test.go @@ -16,8 +16,8 @@ type SampleUser struct { BirthDate time.Time `json:"birth_date,omitempty" jsonschema:"oneof_required=date"` YearOfBirth string `json:"year_of_birth,omitempty" jsonschema:"oneof_required=year"` Metadata interface{} `json:"metadata,omitempty" jsonschema:"oneof_type=string;array"` - IPAddress2 interface{} `json:"ipAddress2,omitempty" jsonschema:"anyof_ref=#/$defs/ipv4;#/$defs/ipv6"` - IPAddresses2 []interface{} `json:"ipAddresses2,omitempty" jsonschema:"anyof_ref=#/$defs/ipv4;#/$defs/ipv6"` + IPAddress2 interface{} `json:"ip_address2,omitempty" jsonschema:"anyof_ref=#/$defs/ipv4;#/$defs/ipv6"` + IPAddresses2 []interface{} `json:"ip_addresses2,omitempty" jsonschema:"anyof_ref=#/$defs/ipv4;#/$defs/ipv6"` FavColor string `json:"fav_color,omitempty" jsonschema:"enum=red,enum=green,enum=blue"` } @@ -95,7 +95,7 @@ func ExampleReflect() { // } // ] // }, - // "ipAddress2": { + // "ip_address2": { // "anyOf": [ // { // "$ref": "#/$defs/ipv4" @@ -105,7 +105,7 @@ func ExampleReflect() { // } // ] // }, - // "ipAddresses2": { + // "ip_addresses2": { // "items": { // "anyOf": [ // { From 584317efcc1e9d37fbb975ab03c3eb297ce73fdc Mon Sep 17 00:00:00 2001 From: Glenn Lewis <6598971+gmlewis@users.noreply.github.com> Date: Wed, 28 Jun 2023 08:51:44 -0400 Subject: [PATCH 5/5] Add unit test coverage Signed-off-by: Glenn Lewis <6598971+gmlewis@users.noreply.github.com> --- fixtures/anyof.json | 13 +++++++++++++ reflect_test.go | 1 + 2 files changed, 14 insertions(+) diff --git a/fixtures/anyof.json b/fixtures/anyof.json index 8b3d4fe..bf768a1 100644 --- a/fixtures/anyof.json +++ b/fixtures/anyof.json @@ -82,6 +82,19 @@ }, "child": { "$ref": "#/$defs/ChildAnyOf" + }, + "field6": { + "anyOf": [ + { + "$ref": "Outer" + }, + { + "$ref": "OuterNamed" + }, + { + "$ref": "OuterPtr" + } + ] } }, "additionalProperties": false, diff --git a/reflect_test.go b/reflect_test.go index c0d2c9f..e36213e 100644 --- a/reflect_test.go +++ b/reflect_test.go @@ -153,6 +153,7 @@ type RootAnyOf struct { Field3 interface{} `json:"field3" jsonschema:"anyof_type=string;array"` Field4 string `json:"field4" jsonschema:"anyof_required=group1"` Field5 ChildAnyOf `json:"child"` + Field6 interface{} `json:"field6" jsonschema:"anyof_ref=Outer;OuterNamed;OuterPtr"` } type ChildAnyOf struct {