Skip to content

Commit ba2ee13

Browse files
committed
fix: improve JSON tag handling and validation logic to support omitzero
and fix not required filed object validation test: add new tests for omitzero tag handling and nil field validation
1 parent 8e5611c commit ba2ee13

File tree

4 files changed

+97
-4
lines changed

4 files changed

+97
-4
lines changed

jsonschema/json.go

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -174,8 +174,18 @@ func reflectSchemaObject(t reflect.Type, defs map[string]Definition) (*Definitio
174174
continue
175175
case jsonTag == "":
176176
jsonTag = field.Name
177-
case strings.HasSuffix(jsonTag, ",omitempty"):
178-
jsonTag = strings.TrimSuffix(jsonTag, ",omitempty")
177+
// case strings.HasSuffix(jsonTag, ",omitempty"):
178+
// jsonTag = strings.TrimSuffix(jsonTag, ",omitempty")
179+
// required = false
180+
// omitempty may not be the end of tag
181+
case strings.Contains(jsonTag, ",omitempty"):
182+
// remove ,omitempty
183+
jsonTag = strings.ReplaceAll(jsonTag, ",omitempty", "")
184+
required = false
185+
// and also omitzero
186+
case strings.Contains(jsonTag, ",omitzero"):
187+
// remove ,omitzero
188+
jsonTag = strings.ReplaceAll(jsonTag, ",omitzero", "")
179189
required = false
180190
}
181191

@@ -197,6 +207,11 @@ func reflectSchemaObject(t reflect.Type, defs map[string]Definition) (*Definitio
197207
item.Nullable = nullable
198208
}
199209

210+
if strings.HasSuffix(jsonTag, ",string") {
211+
jsonTag = strings.TrimSuffix(jsonTag, ",string")
212+
item.Type = String
213+
}
214+
200215
properties[jsonTag] = *item
201216

202217
if s := field.Tag.Get("required"); s != "" {

jsonschema/json_test.go

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -213,6 +213,12 @@ func TestStructToSchema(t *testing.T) {
213213
SnakeCase string `json:"snake_case" required:"true" description:"SnakeCase"`
214214
}
215215

216+
type NewTagOmitzero struct {
217+
ID uint64 `json:"id,omitzero,string"`
218+
Name string `json:"name"`
219+
Age int `json:"age,omitzero"`
220+
}
221+
216222
tests := []struct {
217223
name string
218224
in any
@@ -618,6 +624,28 @@ func TestStructToSchema(t *testing.T) {
618624
"additionalProperties": false
619625
}
620626
}
627+
}`,
628+
},
629+
{
630+
name: "Test int to string json and omitzero",
631+
in: NewTagOmitzero{},
632+
want: `{
633+
"type": "object",
634+
"properties": {
635+
"id": {
636+
"type": "string"
637+
},
638+
"name": {
639+
"type": "string"
640+
},
641+
"age": {
642+
"type": "integer"
643+
}
644+
},
645+
"required": [
646+
"name"
647+
],
648+
"additionalProperties": false
621649
}`,
622650
},
623651
}

jsonschema/validate.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -102,13 +102,14 @@ func validateObject(schema Definition, data any, defs map[string]Definition) boo
102102
return false
103103
}
104104
for _, field := range schema.Required {
105-
if _, exists := dataMap[field]; !exists {
105+
if value, exists := dataMap[field]; !exists || value == nil {
106106
return false
107107
}
108108
}
109109
for key, valueSchema := range schema.Properties {
110110
value, exists := dataMap[key]
111-
if exists && !Validate(valueSchema, value, WithDefs(defs)) {
111+
// if value is required and not exists or nil, should return false before
112+
if exists && value != nil && !Validate(valueSchema, value, WithDefs(defs)) {
112113
return false
113114
} else if !exists && contains(schema.Required, key) {
114115
return false

jsonschema/validate_test.go

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,55 @@ func Test_Validate(t *testing.T) {
161161
},
162162
},
163163
}}, false},
164+
{
165+
"test not required nil filed",
166+
args{
167+
data: map[string]any{
168+
"name": "John",
169+
"age": 28,
170+
"email": nil,
171+
},
172+
schema: jsonschema.Definition{
173+
Type: jsonschema.Object,
174+
Properties: map[string]jsonschema.Definition{
175+
"name": {Type: jsonschema.String},
176+
"age": {Type: jsonschema.Integer},
177+
"email": {Type: jsonschema.String},
178+
},
179+
Required: []string{"name", "age"},
180+
},
181+
},
182+
true,
183+
},
184+
{
185+
"test not required nil object",
186+
args{
187+
data: map[string]any{
188+
"name": "John",
189+
"age": 28,
190+
"address": nil,
191+
},
192+
schema: jsonschema.Definition{
193+
Type: jsonschema.Object,
194+
Properties: map[string]jsonschema.Definition{
195+
"name": {Type: jsonschema.String},
196+
"age": {Type: jsonschema.Integer},
197+
"address": {
198+
Type: jsonschema.Object,
199+
Properties: map[string]jsonschema.Definition{
200+
"street": {Type: jsonschema.String},
201+
"city": {Type: jsonschema.String},
202+
"state": {Type: jsonschema.String},
203+
"zip": {Type: jsonschema.String},
204+
},
205+
Required: []string{"street", "city", "state", "zip"},
206+
},
207+
},
208+
Required: []string{"name", "age"},
209+
},
210+
},
211+
true,
212+
},
164213
}
165214
for _, tt := range tests {
166215
t.Run(tt.name, func(t *testing.T) {

0 commit comments

Comments
 (0)