From d10980697d18987570e0a5630916ec55224a905b Mon Sep 17 00:00:00 2001 From: candiduslynx Date: Mon, 2 Oct 2023 11:47:22 +0300 Subject: [PATCH] fix unsigned int handling (0 is a valid value) --- fixtures/unsigned_int_handling.json | 47 +++++++++++++++++++++++++++++ reflect.go | 41 ++++++++++++++----------- reflect_test.go | 16 ++++++++++ 3 files changed, 86 insertions(+), 18 deletions(-) create mode 100644 fixtures/unsigned_int_handling.json diff --git a/fixtures/unsigned_int_handling.json b/fixtures/unsigned_int_handling.json new file mode 100644 index 0000000..f5d407f --- /dev/null +++ b/fixtures/unsigned_int_handling.json @@ -0,0 +1,47 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://github.com/invopop/jsonschema/unsigned-int-handler", + "$ref": "#/$defs/UnsignedIntHandler", + "$defs": { + "UnsignedIntHandler": { + "properties": { + "min_len": { + "items": { + "type": "string", + "minLength": 0 + }, + "type": "array" + }, + "max_len": { + "items": { + "type": "string", + "maxLength": 0 + }, + "type": "array" + }, + "min_items": { + "items": { + "type": "string" + }, + "type": "array", + "minItems": 0 + }, + "max_items": { + "items": { + "type": "string" + }, + "type": "array", + "maxItems": 0 + } + }, + "additionalProperties": false, + "type": "object", + "required": [ + "min_len", + "max_len", + "min_items", + "max_items" + ] + } + } +} \ No newline at end of file diff --git a/reflect.go b/reflect.go index 4dace60..d5935e8 100644 --- a/reflect.go +++ b/reflect.go @@ -61,16 +61,16 @@ type Schema struct { ExclusiveMaximum json.Number `json:"exclusiveMaximum,omitempty"` // section 6.2.3 Minimum json.Number `json:"minimum,omitempty"` // section 6.2.4 ExclusiveMinimum json.Number `json:"exclusiveMinimum,omitempty"` // section 6.2.5 - MaxLength int `json:"maxLength,omitempty"` // section 6.3.1 - MinLength int `json:"minLength,omitempty"` // section 6.3.2 + MaxLength *uint64 `json:"maxLength,omitempty"` // section 6.3.1 + MinLength *uint64 `json:"minLength,omitempty"` // section 6.3.2 Pattern string `json:"pattern,omitempty"` // section 6.3.3 - MaxItems int `json:"maxItems,omitempty"` // section 6.4.1 - MinItems int `json:"minItems,omitempty"` // section 6.4.2 + MaxItems *uint64 `json:"maxItems,omitempty"` // section 6.4.1 + MinItems *uint64 `json:"minItems,omitempty"` // section 6.4.2 UniqueItems bool `json:"uniqueItems,omitempty"` // section 6.4.3 - MaxContains uint `json:"maxContains,omitempty"` // section 6.4.4 - MinContains uint `json:"minContains,omitempty"` // section 6.4.5 - MaxProperties int `json:"maxProperties,omitempty"` // section 6.5.1 - MinProperties int `json:"minProperties,omitempty"` // section 6.5.2 + MaxContains *uint64 `json:"maxContains,omitempty"` // section 6.4.4 + MinContains *uint64 `json:"minContains,omitempty"` // section 6.4.5 + MaxProperties *uint64 `json:"maxProperties,omitempty"` // section 6.5.1 + MinProperties *uint64 `json:"minProperties,omitempty"` // section 6.5.2 Required []string `json:"required,omitempty"` // section 6.5.3 DependentRequired map[string][]string `json:"dependentRequired,omitempty"` // section 6.5.4 // RFC draft-bhutton-json-schema-validation-00, section 7 @@ -459,8 +459,9 @@ func (r *Reflector) reflectSliceOrArray(definitions Definitions, t reflect.Type, } if t.Kind() == reflect.Array { - st.MinItems = t.Len() - st.MaxItems = st.MinItems + l := uint64(t.Len()) + st.MinItems = &l + st.MaxItems = &l } if t.Kind() == reflect.Slice && t.Elem() == byteSliceType.Elem() { st.Type = "string" @@ -808,11 +809,9 @@ func (t *Schema) stringKeywords(tags []string) { name, val := nameValue[0], nameValue[1] switch name { case "minLength": - i, _ := strconv.Atoi(val) - t.MinLength = i + t.MinLength = parseUint(val) case "maxLength": - i, _ := strconv.Atoi(val) - t.MaxLength = i + t.MaxLength = parseUint(val) case "pattern": t.Pattern = val case "format": @@ -898,11 +897,9 @@ func (t *Schema) arrayKeywords(tags []string) { name, val := nameValue[0], nameValue[1] switch name { case "minItems": - i, _ := strconv.Atoi(val) - t.MinItems = i + t.MinItems = parseUint(val) case "maxItems": - i, _ := strconv.Atoi(val) - t.MaxItems = i + t.MaxItems = parseUint(val) case "uniqueItems": t.UniqueItems = true case "default": @@ -1039,6 +1036,14 @@ func toJSONNumber(s string) (json.Number, bool) { return json.Number(""), false } +func parseUint(num string) *uint64 { + val, err := strconv.ParseUint(num, 10, 64) + if err != nil { + return nil + } + return &val +} + func (r *Reflector) fieldNameTag() string { if r.FieldNameTag != "" { return r.FieldNameTag diff --git a/reflect_test.go b/reflect_test.go index d463097..a00fc16 100644 --- a/reflect_test.go +++ b/reflect_test.go @@ -600,3 +600,19 @@ func TestArrayHandling(t *testing.T) { fixtureContains(t, "fixtures/array_handling.json", `"minLength": 2`) fixtureContains(t, "fixtures/array_handling.json", `"minimum": 2.5`) } + +func TestUnsignedIntHandling(t *testing.T) { + type UnsignedIntHandler struct { + MinLen []string `json:"min_len" jsonschema:"minLength=0"` + MaxLen []string `json:"max_len" jsonschema:"maxLength=0"` + MinItems []string `json:"min_items" jsonschema:"minItems=0"` + MaxItems []string `json:"max_items" jsonschema:"maxItems=0"` + } + + r := &Reflector{} + compareSchemaOutput(t, "fixtures/unsigned_int_handling.json", r, &UnsignedIntHandler{}) + fixtureContains(t, "fixtures/unsigned_int_handling.json", `"minLength": 0`) + fixtureContains(t, "fixtures/unsigned_int_handling.json", `"maxLength": 0`) + fixtureContains(t, "fixtures/unsigned_int_handling.json", `"minItems": 0`) + fixtureContains(t, "fixtures/unsigned_int_handling.json", `"maxItems": 0`) +}