@@ -9,6 +9,7 @@ package jsonschema
9
9
import (
10
10
"bytes"
11
11
"encoding/json"
12
+ "fmt"
12
13
"net"
13
14
"net/url"
14
15
"reflect"
@@ -295,8 +296,8 @@ func (r *Reflector) reflectTypeToSchema(definitions Definitions, t reflect.Type)
295
296
// It will unmarshal either.
296
297
if t .Implements (protoEnumType ) {
297
298
st .OneOf = []* Schema {
298
- {Type : "string" },
299
- {Type : "integer" },
299
+ {Type : & Type { Types : [] string { "string" }} },
300
+ {Type : & Type { Types : [] string { "integer" }} },
300
301
}
301
302
return st
302
303
}
@@ -306,7 +307,7 @@ func (r *Reflector) reflectTypeToSchema(definitions Definitions, t reflect.Type)
306
307
// TODO email RFC section 7.3.2, hostname RFC section 7.3.3, uriref RFC section 7.3.7
307
308
if t == ipType {
308
309
// TODO differentiate ipv4 and ipv6 RFC section 7.3.4, 7.3.5
309
- st .Type = "string"
310
+ st .Type = & Type { Types : [] string { "string" }}
310
311
st .Format = "ipv4"
311
312
return st
312
313
}
@@ -326,16 +327,16 @@ func (r *Reflector) reflectTypeToSchema(definitions Definitions, t reflect.Type)
326
327
327
328
case reflect .Int , reflect .Int8 , reflect .Int16 , reflect .Int32 , reflect .Int64 ,
328
329
reflect .Uint , reflect .Uint8 , reflect .Uint16 , reflect .Uint32 , reflect .Uint64 :
329
- st .Type = "integer"
330
+ st .Type = & Type { Types : [] string { "integer" }}
330
331
331
332
case reflect .Float32 , reflect .Float64 :
332
- st .Type = "number"
333
+ st .Type = & Type { Types : [] string { "number" }}
333
334
334
335
case reflect .Bool :
335
- st .Type = "boolean"
336
+ st .Type = & Type { Types : [] string { "boolean" }}
336
337
337
338
case reflect .String :
338
- st .Type = "string"
339
+ st .Type = & Type { Types : [] string { "string" }}
339
340
340
341
default :
341
342
panic ("unsupported type " + t .String ())
@@ -400,19 +401,19 @@ func (r *Reflector) reflectSliceOrArray(definitions Definitions, t reflect.Type,
400
401
st .MaxItems = & l
401
402
}
402
403
if t .Kind () == reflect .Slice && t .Elem () == byteSliceType .Elem () {
403
- st .Type = "string"
404
+ st .Type = & Type { Types : [] string { "string" }}
404
405
// NOTE: ContentMediaType is not set here
405
406
st .ContentEncoding = "base64"
406
407
} else {
407
- st .Type = "array"
408
+ st .Type = & Type { Types : [] string { "array" }}
408
409
st .Items = r .refOrReflectTypeToSchema (definitions , t .Elem ())
409
410
}
410
411
}
411
412
412
413
func (r * Reflector ) reflectMap (definitions Definitions , t reflect.Type , st * Schema ) {
413
414
r .addDefinition (definitions , t , st )
414
415
415
- st .Type = "object"
416
+ st .Type = & Type { Types : [] string { "object" }}
416
417
if st .Description == "" {
417
418
st .Description = r .lookupComment (t , "" )
418
419
}
@@ -435,17 +436,17 @@ func (r *Reflector) reflectStruct(definitions Definitions, t reflect.Type, s *Sc
435
436
// Handle special types
436
437
switch t {
437
438
case timeType : // date-time RFC section 7.3.1
438
- s .Type = "string"
439
+ s .Type = & Type { Types : [] string { "string" }}
439
440
s .Format = "date-time"
440
441
return
441
442
case uriType : // uri RFC section 7.3.6
442
- s .Type = "string"
443
+ s .Type = & Type { Types : [] string { "string" }}
443
444
s .Format = "uri"
444
445
return
445
446
}
446
447
447
448
r .addDefinition (definitions , t , s )
448
- s .Type = "object"
449
+ s .Type = & Type { Types : [] string { "object" }}
449
450
s .Properties = NewProperties ()
450
451
s .Description = r .lookupComment (t , "" )
451
452
if r .AssignAnchor {
@@ -524,7 +525,7 @@ func (r *Reflector) reflectStructFields(st *Schema, definitions Definitions, t r
524
525
OneOf : []* Schema {
525
526
property ,
526
527
{
527
- Type : "null" ,
528
+ Type : & Type { Types : [] string { "null" }} ,
528
529
},
529
530
},
530
531
}
@@ -614,18 +615,23 @@ func (t *Schema) structKeywordsFromTags(f reflect.StructField, parent *Schema, p
614
615
tags := splitOnUnescapedCommas (f .Tag .Get ("jsonschema" ))
615
616
tags = t .genericKeywords (tags , parent , propertyName )
616
617
617
- switch t .Type {
618
- case "string" :
619
- t .stringKeywords (tags )
620
- case "number" :
621
- t .numericalKeywords (tags )
622
- case "integer" :
623
- t .numericalKeywords (tags )
624
- case "array" :
625
- t .arrayKeywords (tags )
626
- case "boolean" :
627
- t .booleanKeywords (tags )
618
+ if t .Type != nil {
619
+ for _ , currType := range t .Type .Types {
620
+ switch currType {
621
+ case "string" :
622
+ t .stringKeywords (tags )
623
+ case "number" :
624
+ t .numericalKeywords (tags )
625
+ case "integer" :
626
+ t .numericalKeywords (tags )
627
+ case "array" :
628
+ t .arrayKeywords (tags )
629
+ case "boolean" :
630
+ t .booleanKeywords (tags )
631
+ }
632
+ }
628
633
}
634
+
629
635
extras := strings .Split (f .Tag .Get ("jsonschema_extras" ), "," )
630
636
t .extraKeywords (extras )
631
637
}
@@ -643,7 +649,8 @@ func (t *Schema) genericKeywords(tags []string, parent *Schema, propertyName str
643
649
case "description" :
644
650
t .Description = val
645
651
case "type" :
646
- t .Type = val
652
+ types := strings .Split (val , ";" )
653
+ t .Type = & Type {Types : types }
647
654
case "anchor" :
648
655
t .Anchor = val
649
656
case "oneof_required" :
@@ -695,11 +702,11 @@ func (t *Schema) genericKeywords(tags []string, parent *Schema, propertyName str
695
702
if t .OneOf == nil {
696
703
t .OneOf = make ([]* Schema , 0 , 1 )
697
704
}
698
- t .Type = ""
705
+ t .Type = nil
699
706
types := strings .Split (nameValue [1 ], ";" )
700
707
for _ , ty := range types {
701
708
t .OneOf = append (t .OneOf , & Schema {
702
- Type : ty ,
709
+ Type : & Type { Types : [] string { ty }} ,
703
710
})
704
711
}
705
712
case "anyof_ref" :
@@ -721,11 +728,11 @@ func (t *Schema) genericKeywords(tags []string, parent *Schema, propertyName str
721
728
if t .AnyOf == nil {
722
729
t .AnyOf = make ([]* Schema , 0 , 1 )
723
730
}
724
- t .Type = ""
731
+ t .Type = nil
725
732
types := strings .Split (nameValue [1 ], ";" )
726
733
for _ , ty := range types {
727
734
t .AnyOf = append (t .AnyOf , & Schema {
728
- Type : ty ,
735
+ Type : & Type { Types : [] string { ty }} ,
729
736
})
730
737
}
731
738
default :
@@ -872,17 +879,23 @@ func (t *Schema) arrayKeywords(tags []string) {
872
879
return
873
880
}
874
881
875
- switch t .Items .Type {
876
- case "string" :
877
- t .Items .stringKeywords (unprocessed )
878
- case "number" :
879
- t .Items .numericalKeywords (unprocessed )
880
- case "integer" :
881
- t .Items .numericalKeywords (unprocessed )
882
- case "array" :
883
- // explicitly don't support traversal for the [][]..., as it's unclear where the array tags belong
884
- case "boolean" :
885
- t .Items .booleanKeywords (unprocessed )
882
+ if t .Items .Type == nil {
883
+ return
884
+ }
885
+
886
+ for _ , currType := range t .Items .Type .Types {
887
+ switch currType {
888
+ case "string" :
889
+ t .Items .stringKeywords (unprocessed )
890
+ case "number" :
891
+ t .Items .numericalKeywords (unprocessed )
892
+ case "integer" :
893
+ t .Items .numericalKeywords (unprocessed )
894
+ case "array" :
895
+ // explicitly don't support traversal for the [][]..., as it's unclear where the array tags belong
896
+ case "boolean" :
897
+ t .Items .booleanKeywords (unprocessed )
898
+ }
886
899
}
887
900
}
888
901
@@ -1112,6 +1125,30 @@ func (t *Schema) MarshalJSON() ([]byte, error) {
1112
1125
return append (b , m [1 :]... ), nil
1113
1126
}
1114
1127
1128
+ func (tp Type ) MarshalJSON () ([]byte , error ) {
1129
+ switch len (tp .Types ) {
1130
+ case 0 :
1131
+ return nil , nil
1132
+ case 1 :
1133
+ return json .Marshal (tp .Types [0 ])
1134
+ default :
1135
+ return json .Marshal (tp .Types )
1136
+ }
1137
+ }
1138
+
1139
+ func (tp * Type ) UnmarshalJSON (data []byte ) error {
1140
+ err := json .Unmarshal (data , & tp .Types )
1141
+ if err == nil {
1142
+ return nil
1143
+ }
1144
+ var v string
1145
+ err2 := json .Unmarshal (data , & v )
1146
+ if err2 != nil {
1147
+ return fmt .Errorf ("could not read type into slice: %v, nor into string: %w" , err .Error (), err2 )
1148
+ }
1149
+ return nil
1150
+ }
1151
+
1115
1152
func (r * Reflector ) typeName (t reflect.Type ) string {
1116
1153
if r .Namer != nil {
1117
1154
if name := r .Namer (t ); name != "" {
0 commit comments