Skip to content

Commit a99baf2

Browse files
committed
feat: initial support for local references
admittedly, dealing with references properly has taken longer than I'd initially anticipated. So far the best solution I can come up with requires two changes: 1. move away from a master struct to keeping slices of validators 2. implement json-pointer traversal for decoded go types The implementation of string token traversal will take some time to get right, but the bones are there. This also sets the stage for adding user-defined validators, which sounds nice
1 parent c193501 commit a99baf2

File tree

11 files changed

+735
-247
lines changed

11 files changed

+735
-247
lines changed
File renamed without changes.

README.md

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,10 @@
1+
# jsonschema
12
[![Qri](https://img.shields.io/badge/made%20by-qri-magenta.svg?style=flat-square)](https://qri.io)
23
[![GoDoc](https://godoc.org/github.com/qri-io/jsonschema?status.svg)](http://godoc.org/github.com/qri-io/jsonschema)
34
[![License](https://img.shields.io/github/license/qri-io/jsonschema.svg?style=flat-square)](./LICENSE)
45
[![Codecov](https://img.shields.io/codecov/c/github/qri-io/jsonschema.svg?style=flat-square)](https://codecov.io/gh/qri-io/jsonschema)
56
[![CI](https://img.shields.io/circleci/project/github/qri-io/jsonschema.svg?style=flat-square)](https://circleci.com/gh/qri-io/jsonschema)
67
[![Go Report Card](https://goreportcard.com/badge/github.com/qri-io/jsonschema)](https://goreportcard.com/report/github.com/qri-io/jsonschema)
78

8-
9-
# jsonschema
10-
11-
Provides utility for converting lengthy titles into condensed but still recognizable variable names
12-
139
### 🚧🚧 Under Construction 🚧🚧
1410
golang implementation of http://json-schema.org/

keywords.go

Lines changed: 84 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,44 @@ import (
44
"encoding/json"
55
"fmt"
66
"reflect"
7+
"strconv"
78
)
89

10+
// primitiveTypes is a map of strings to check types against
11+
var primitiveTypes = map[string]bool{
12+
"null": true,
13+
"boolean": true,
14+
"object": true,
15+
"array": true,
16+
"number": true,
17+
"string": true,
18+
"integer": true,
19+
}
20+
21+
// DataType gives the primitive json type of a value, plus the special case
22+
// "integer" for when numbers are whole
23+
func DataType(data interface{}) string {
24+
switch v := data.(type) {
25+
case nil:
26+
return "null"
27+
case bool:
28+
return "boolean"
29+
case float64:
30+
if float64(int(v)) == v {
31+
return "integer"
32+
}
33+
return "number"
34+
case string:
35+
return "string"
36+
case []interface{}:
37+
return "array"
38+
case map[string]interface{}:
39+
return "object"
40+
default:
41+
return "unknown"
42+
}
43+
}
44+
945
// Type specifies one of the six json primitive types.
1046
// The value of this keyword MUST be either a string or an array.
1147
// If it is an array, elements of the array MUST be strings and MUST be unique.
@@ -22,18 +58,27 @@ func (t Type) Validate(data interface{}) error {
2258
return nil
2359
}
2460
}
25-
return fmt.Errorf(`expected "%v" to be a %s`, data, jt)
61+
if len(t) == 1 {
62+
return fmt.Errorf(`expected "%v" to be of type %s`, data, t[0])
63+
} else {
64+
str := ""
65+
for _, ts := range t {
66+
str += ts + ","
67+
}
68+
return fmt.Errorf(`expected "%v" to be one of type: %s`, data, str[:len(str)-1])
69+
}
2670
}
2771

28-
// primitiveTypes is a map of strings to check types against
29-
var primitiveTypes = map[string]bool{
30-
"null": true,
31-
"boolean": true,
32-
"object": true,
33-
"array": true,
34-
"number": true,
35-
"string": true,
36-
"integer": true,
72+
// JSONProp implements JSON property name indexing for Type
73+
func (t Type) JSONProp(name string) interface{} {
74+
idx, err := strconv.Atoi(name)
75+
if err != nil {
76+
return nil
77+
}
78+
if idx > len(t) || idx < 0 {
79+
return nil
80+
}
81+
return t[idx]
3782
}
3883

3984
// UnmarshalJSON implements the json.Unmarshaler interface for Type
@@ -93,30 +138,48 @@ func (e Enum) Validate(data interface{}) error {
93138
return fmt.Errorf("expected %s to be one of %s", data)
94139
}
95140

141+
// JSONProp implements JSON property name indexing for Enum
142+
func (e Enum) JSONProp(name string) interface{} {
143+
idx, err := strconv.Atoi(name)
144+
if err != nil {
145+
return nil
146+
}
147+
if idx > len(e) || idx < 0 {
148+
return nil
149+
}
150+
return e[idx]
151+
}
152+
96153
// Const MAY be of any type, including null.
97154
// An instance validates successfully against this keyword if its
98155
// value is equal to the value of the keyword.
99156
type Const []byte
100157

101-
// String implements the Stringer interface for Const
102-
func (c Const) String() string {
103-
return string(c)
104-
}
105-
106-
// UnmarshalJSON implements the json.Unmarshaler interface for Const
107-
func (c *Const) UnmarshalJSON(data []byte) error {
108-
*c = data
109-
return nil
110-
}
111-
112158
// Validate implements the validate interface for Const
113159
func (c Const) Validate(data interface{}) error {
114160
var con interface{}
115161
if err := json.Unmarshal(c, &con); err != nil {
116162
return err
117163
}
164+
118165
if !reflect.DeepEqual(con, data) {
119166
return fmt.Errorf(`%s must equal %s`, string(c), data)
120167
}
121168
return nil
122169
}
170+
171+
// JSONProp implements JSON property name indexing for Const
172+
func (c Const) JSONProp(name string) interface{} {
173+
return nil
174+
}
175+
176+
// String implements the Stringer interface for Const
177+
func (c Const) String() string {
178+
return string(c)
179+
}
180+
181+
// UnmarshalJSON implements the json.Unmarshaler interface for Const
182+
func (c *Const) UnmarshalJSON(data []byte) error {
183+
*c = data
184+
return nil
185+
}

keywords_arrays.go

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"encoding/json"
55
"fmt"
66
"reflect"
7+
"strconv"
78
)
89

910
// Items MUST be either a valid JSON Schema or an array of valid JSON Schemas.
@@ -27,14 +28,14 @@ func (it Items) Validate(data interface{}) error {
2728
if it.single {
2829
for i, elem := range arr {
2930
if err := it.Schemas[0].Validate(elem); err != nil {
30-
return fmt.Errorf("element %d: %s", i, err.Error())
31+
return fmt.Errorf("element %d %s", i, err.Error())
3132
}
3233
}
3334
} else {
3435
for i, vs := range it.Schemas {
3536
if i < len(arr) {
3637
if err := vs.Validate(arr[i]); err != nil {
37-
return fmt.Errorf("element %d: %s", i, err.Error())
38+
return fmt.Errorf("element %d %s", i, err.Error())
3839
}
3940
}
4041
}
@@ -43,6 +44,26 @@ func (it Items) Validate(data interface{}) error {
4344
return nil
4445
}
4546

47+
// JSONProp implements JSON property name indexing for Items
48+
func (it Items) JSONProp(name string) interface{} {
49+
idx, err := strconv.Atoi(name)
50+
if err != nil {
51+
return nil
52+
}
53+
if idx > len(it.Schemas) || idx < 0 {
54+
return nil
55+
}
56+
return it.Schemas[idx]
57+
}
58+
59+
func (it Items) JSONChildren() (res map[string]JSONPather) {
60+
res = map[string]JSONPather{}
61+
for i, sch := range it.Schemas {
62+
res[strconv.Itoa(i)] = sch
63+
}
64+
return
65+
}
66+
4667
// UnmarshalJSON implements the json.Unmarshaler interface for Items
4768
func (it *Items) UnmarshalJSON(data []byte) error {
4869
s := &Schema{}
@@ -94,6 +115,11 @@ func (a *AdditionalItems) Validate(data interface{}) error {
94115
return nil
95116
}
96117

118+
// JSONProp implements JSON property name indexing for AdditionalItems
119+
func (a *AdditionalItems) JSONProp(name string) interface{} {
120+
return a.Schema.JSONProp(name)
121+
}
122+
97123
// UnmarshalJSON implements the json.Unmarshaler interface for AdditionalItems
98124
func (a *AdditionalItems) UnmarshalJSON(data []byte) error {
99125
sch := &Schema{}
@@ -175,6 +201,11 @@ func (c *Contains) Validate(data interface{}) error {
175201
return nil
176202
}
177203

204+
// JSONProp implements JSON property name indexing for Contains
205+
func (m Contains) JSONProp(name string) interface{} {
206+
return Schema(m).JSONProp(name)
207+
}
208+
178209
// UnmarshalJSON implements the json.Unmarshaler interface for Contains
179210
func (c *Contains) UnmarshalJSON(data []byte) error {
180211
var sch Schema

keywords_booleans.go

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package jsonschema
33
import (
44
"encoding/json"
55
"fmt"
6+
"strconv"
67
)
78

89
// AllOf MUST be a non-empty array. Each item of the array MUST be a valid JSON Schema.
@@ -19,6 +20,18 @@ func (a AllOf) Validate(data interface{}) error {
1920
return nil
2021
}
2122

23+
// JSONProp implements JSON property name indexing for AllOf
24+
func (a AllOf) JSONProp(name string) interface{} {
25+
idx, err := strconv.Atoi(name)
26+
if err != nil {
27+
return nil
28+
}
29+
if idx > len(a) || idx < 0 {
30+
return nil
31+
}
32+
return a[idx]
33+
}
34+
2235
// AnyOf MUST be a non-empty array. Each item of the array MUST be a valid JSON Schema.
2336
// An instance validates successfully against this keyword if it validates successfully against at
2437
// least one schema defined by this keyword's value.
@@ -34,6 +47,18 @@ func (a AnyOf) Validate(data interface{}) error {
3447
return fmt.Errorf("value did not match any specified anyOf schemas: %v", data)
3548
}
3649

50+
// JSONProp implements JSON property name indexing for AnyOf
51+
func (a AnyOf) JSONProp(name string) interface{} {
52+
idx, err := strconv.Atoi(name)
53+
if err != nil {
54+
return nil
55+
}
56+
if idx > len(a) || idx < 0 {
57+
return nil
58+
}
59+
return a[idx]
60+
}
61+
3762
// OneOf MUST be a non-empty array. Each item of the array MUST be a valid JSON Schema.
3863
// An instance validates successfully against this keyword if it validates successfully against exactly one schema defined by this keyword's value.
3964
type OneOf []*Schema
@@ -55,6 +80,18 @@ func (o OneOf) Validate(data interface{}) error {
5580
return nil
5681
}
5782

83+
// JSONProp implements JSON property name indexing for OneOf
84+
func (o OneOf) JSONProp(name string) interface{} {
85+
idx, err := strconv.Atoi(name)
86+
if err != nil {
87+
return nil
88+
}
89+
if idx > len(o) || idx < 0 {
90+
return nil
91+
}
92+
return o[idx]
93+
}
94+
5895
// Not MUST be a valid JSON Schema.
5996
// An instance is valid against this keyword if it fails to validate successfully against the schema defined
6097
// by this keyword.
@@ -70,6 +107,11 @@ func (n *Not) Validate(data interface{}) error {
70107
return nil
71108
}
72109

110+
// JSONProp implements JSON property name indexing for Not
111+
func (n Not) JSONProp(name string) interface{} {
112+
return Schema(n).JSONProp(name)
113+
}
114+
73115
// UnmarshalJSON implements the json.Unmarshaler interface for Not
74116
func (n *Not) UnmarshalJSON(data []byte) error {
75117
var sch Schema

keywords_conditionals.go

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,11 @@ func (i *If) Validate(data interface{}) error {
1515
return nil
1616
}
1717

18+
// JSONProp implements JSON property name indexing for If
19+
func (i If) JSONProp(name string) interface{} {
20+
return Schema(i).JSONProp(name)
21+
}
22+
1823
// UnmarshalJSON implements the json.Unmarshaler interface for If
1924
func (i *If) UnmarshalJSON(data []byte) error {
2025
var sch Schema
@@ -35,6 +40,11 @@ func (t *Then) Validate(data interface{}) error {
3540
return nil
3641
}
3742

43+
// JSONProp implements JSON property name indexing for Then
44+
func (t Then) JSONProp(name string) interface{} {
45+
return Schema(t).JSONProp(name)
46+
}
47+
3848
// UnmarshalJSON implements the json.Unmarshaler interface for Then
3949
func (t *Then) UnmarshalJSON(data []byte) error {
4050
var sch Schema
@@ -55,6 +65,11 @@ func (e *Else) Validate(data interface{}) error {
5565
return nil
5666
}
5767

68+
// JSONProp implements JSON property name indexing for Else
69+
func (e Else) JSONProp(name string) interface{} {
70+
return Schema(e).JSONProp(name)
71+
}
72+
5873
// UnmarshalJSON implements the json.Unmarshaler interface for Else
5974
func (e *Else) UnmarshalJSON(data []byte) error {
6075
var sch Schema

0 commit comments

Comments
 (0)