json-schema 0.1.12 → 0.1.13

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,6 +1,6 @@
1
1
  h1. Ruby JSON Schema Validator
2
2
 
3
- This library is intended to provide Ruby with an interface for validating JSON objects against a JSON schema conforming to "JSON Schema Draft 3":http://tools.ietf.org/html/draft-zyp-json-schema-03. The project originally started as a fork of "Constellation's ruby-jsonschema":https://github.com/Constellation/ruby-jsonchema project, but differences in the JSON schema draft versions implemented as well as assumptions made by the ruby-jsonschema library forced this to become a new project.
3
+ This library is intended to provide Ruby with an interface for validating JSON objects against a JSON schema conforming to "JSON Schema Draft 3":http://tools.ietf.org/html/draft-zyp-json-schema-03.
4
4
 
5
5
  h2. Usage
6
6
 
@@ -13,8 +13,8 @@ gem install json-schema
13
13
  If downloading the git repo, build the gem and install it:
14
14
 
15
15
  <pre>
16
- $ rake package
17
- $ gem install pkg/json-schema-0.1.6.gem
16
+ $ gem build json-schema.gemspec
17
+ $ gem install json-schema-0.1.13.gem
18
18
  </pre>
19
19
 
20
20
  <pre>
@@ -51,56 +51,21 @@ end
51
51
  </pre>
52
52
 
53
53
 
54
- h2. Currently implemented
55
-
56
- The following core schema definitions are currently implemented:
57
-
58
- * type
59
- * properties
60
- * patternProperties
61
- * additionalProperties
62
- * items
63
- * additionalItems
64
- * required
65
- * dependencies
66
- * minimum
67
- * maximum
68
- * exclusiveMinimum
69
- * exclusiveMaximum
70
- * minItems
71
- * maxItems
72
- * uniqueItems
73
- * pattern
74
- * minLength
75
- * maxLength
76
- * enum
77
- * title
78
- * description
79
- * divisibleBy
80
- * disallow
81
- * extends
82
- * id
83
- * $ref (this implementation only follows slash-delimited fragment resolution)
84
-
85
-
86
- h2. Not implemented
87
-
88
- The following core schema definitions are not implemented:
89
-
90
- * format - Some of the formats listed will be supported, others are fairly vague in their definition and may be left out
54
+ h2. Notes
55
+
56
+ The following core schema attributes are not implemented:
57
+
91
58
  * default - This library aims to solely be a validator and does not modify an object it is validating.
92
59
  * $schema - Support for this will come later, possibly with the ability to validate against other JSON Schema draft versions
93
60
 
94
- In addition, the following hyper schema attributes are not implemented at this time:
95
-
96
- * links
97
- * fragmentResolution (only handles default slash-delimited)
98
- * contentEncoding
99
- * pathStart
61
+ The 'format' attribute is only validated for the following values:
100
62
 
63
+ * date-time
64
+ * date
65
+ * time
66
+ * ip-address
67
+ * ipv6
101
68
 
102
- h2. To Do
69
+ All other 'format' attribute values are simply checked to ensure the instance value is of the correct datatype (e.g., an instance value is validated to be an integer or a float in the case of 'utc-millisec').
103
70
 
104
- * (Much) More testing
105
- * Documentation
106
- * Breaking the current validator out into a subclass, acting as the foundation for supporting multiple versions of validators
71
+ Additionally, JSON::Validator does not handle any json hyperschema attributes.
@@ -3,6 +3,7 @@ require 'open-uri'
3
3
  require 'pathname'
4
4
  require 'bigdecimal'
5
5
  require 'digest/sha1'
6
+ require 'date'
6
7
 
7
8
  module JSON
8
9
 
@@ -48,6 +49,7 @@ module JSON
48
49
  "additionalItems",
49
50
  "dependencies",
50
51
  "extends",
52
+ "format",
51
53
  "$ref"
52
54
  ]
53
55
 
@@ -175,6 +177,108 @@ module JSON
175
177
  end
176
178
 
177
179
 
180
+ # Validate the format of an item
181
+ def validate_format(current_schema, data, fragments)
182
+ case current_schema.schema['format']
183
+
184
+ # Timestamp in restricted ISO-8601 YYYY-MM-DDThh:mm:ssZ
185
+ when 'date-time'
186
+ error_message = "The property '#{build_fragment(fragments)}' must be a string and be a date/time in the ISO-8601 format of YYYY-MM-DDThh:mm:ssZ"
187
+ raise ValidationError.new(error_message, fragments, current_schema) if !data.is_a?(String)
188
+ r = Regexp.new('^\d\d\d\d-\d\d-\d\dT(\d\d):(\d\d):(\d\d)Z$')
189
+ if (m = r.match(data))
190
+ parts = data.split("T")
191
+ begin
192
+ Date.parse(parts[0])
193
+ rescue Exception
194
+ raise ValidationError.new(error_message, fragments, current_schema)
195
+ end
196
+ begin
197
+ raise ValidationError.new(error_message, fragments, current_schema) if m[1].to_i > 23
198
+ raise ValidationError.new(error_message, fragments, current_schema) if m[2].to_i > 59
199
+ raise ValidationError.new(error_message, fragments, current_schema) if m[3].to_i > 59
200
+ rescue Exception
201
+ raise ValidationError.new(error_message, fragments, current_schema)
202
+ end
203
+ else
204
+ raise ValidationError.new(error_message, fragments, current_schema)
205
+ end
206
+
207
+ # Date in the format of YYYY-MM-DD
208
+ when 'date'
209
+ error_message = "The property '#{build_fragment(fragments)}' must be a string and be a date in the format of YYYY-MM-DD"
210
+ raise ValidationError.new(error_message, fragments, current_schema) if !data.is_a?(String)
211
+ r = Regexp.new('^\d\d\d\d-\d\d-\d\d$')
212
+ if (m = r.match(data))
213
+ begin
214
+ Date.parse(data)
215
+ rescue Exception
216
+ raise ValidationError.new(error_message, fragments, current_schema)
217
+ end
218
+ else
219
+ raise ValidationError.new(error_message, fragments, current_schema)
220
+ end
221
+
222
+ # Time in the format of HH:MM:SS
223
+ when 'time'
224
+ error_message = "The property '#{build_fragment(fragments)}' must be a string and be a time in the format of hh:mm:ss"
225
+ raise ValidationError.new(error_message, fragments, current_schema) if !data.is_a?(String)
226
+ r = Regexp.new('^(\d\d):(\d\d):(\d\d)$')
227
+ if (m = r.match(data))
228
+ raise ValidationError.new(error_message, fragments, current_schema) if m[1].to_i > 23
229
+ raise ValidationError.new(error_message, fragments, current_schema) if m[2].to_i > 59
230
+ raise ValidationError.new(error_message, fragments, current_schema) if m[3].to_i > 59
231
+ else
232
+ raise ValidationError.new(error_message, fragments, current_schema)
233
+ end
234
+
235
+ # IPv4 in dotted-quad format
236
+ when 'ip-address', 'ipv4'
237
+ error_message = "The property '#{build_fragment(fragments)}' must be a string and be a valid IPv4 address"
238
+ raise ValidationError.new(error_message, fragments, current_schema) if !data.is_a?(String)
239
+ r = Regexp.new('^(\d+){1,3}\.(\d+){1,3}\.(\d+){1,3}\.(\d+){1,3}$')
240
+ if (m = r.match(data))
241
+ 1.upto(4) do |x|
242
+ raise ValidationError.new(error_message, fragments, current_schema) if m[x].to_i > 255
243
+ end
244
+ else
245
+ raise ValidationError.new(error_message, fragments, current_schema)
246
+ end
247
+
248
+ # IPv6 in standard format (including abbreviations)
249
+ when 'ipv6'
250
+ error_message = "The property '#{build_fragment(fragments)}' must be a string and be a valid IPv6 address"
251
+ raise ValidationError.new(error_message, fragments, current_schema) if !data.is_a?(String)
252
+ r = Regexp.new('^[a-f0-9:]+$')
253
+ if (m = r.match(data))
254
+ # All characters are valid, now validate structure
255
+ parts = data.split(":")
256
+ raise ValidationError.new(error_message, fragments, current_schema) if parts.length > 8
257
+ condensed_zeros = false
258
+ parts.each do |part|
259
+ if part.length == 0
260
+ raise ValidationError.new(error_message, fragments, current_schema) if condensed_zeros
261
+ condensed_zeros = true
262
+ end
263
+ raise ValidationError.new(error_message, fragments, current_schema) if part.length > 4
264
+ end
265
+ else
266
+ raise ValidationError.new(error_message, fragments, current_schema)
267
+ end
268
+
269
+ # Milliseconds since the epoch. Must be an integer or a float
270
+ when 'utc-millisec'
271
+ error_message = "The property '#{build_fragment(fragments)}' must be an integer or a float"
272
+ raise ValidationError.new(error_message, fragments, current_schema) if (!data.is_a?(Numeric))
273
+
274
+ # Must be a string
275
+ when 'regex','color','style','phone','uri','email','host-name'
276
+ error_message = "The property '#{build_fragment(fragments)}' must be a string"
277
+ raise ValidationError.new(error_message, fragments, current_schema) if (!data.is_a?(String))
278
+ end
279
+ end
280
+
281
+
178
282
  # Validate the minimum value of a number
179
283
  def validate_minimum(current_schema, data, fragments)
180
284
  if data.is_a?(Numeric)
@@ -744,5 +744,206 @@ class JSONSchemaTest < Test::Unit::TestCase
744
744
 
745
745
  end
746
746
 
747
+
748
+ def test_self_reference
749
+ schema = {
750
+ "type" => "object",
751
+ "properties" => { "a" => {"type" => "integer"}, "b" => {"$ref" => "#"}}
752
+ }
753
+
754
+ data = {"a" => 5, "b" => {"b" => {"a" => 1}}}
755
+ assert(JSON::Validator.validate(schema,data))
756
+ data = {"a" => 5, "b" => {"b" => {"a" => 'taco'}}}
757
+ assert(!JSON::Validator.validate(schema,data))
758
+ end
759
+
760
+
761
+ def test_format_ipv4
762
+ schema = {
763
+ "type" => "object",
764
+ "properties" => { "a" => {"type" => "string", "format" => "ip-address"}}
765
+ }
766
+
767
+ data = {"a" => "1.1.1.1"}
768
+ assert(JSON::Validator.validate(schema,data))
769
+ data = {"a" => "1.1.1"}
770
+ assert(!JSON::Validator.validate(schema,data))
771
+ data = {"a" => "1.1.1.300"}
772
+ assert(!JSON::Validator.validate(schema,data))
773
+ data = {"a" => 5}
774
+ assert(!JSON::Validator.validate(schema,data))
775
+ data = {"a" => "1.1.1"}
776
+ assert(!JSON::Validator.validate(schema,data))
777
+ data = {"a" => "1.1.1.1b"}
778
+ assert(!JSON::Validator.validate(schema,data))
779
+ data = {"a" => "b1.1.1.1"}
780
+ end
781
+
782
+
783
+ def test_format_ipv6
784
+ schema = {
785
+ "type" => "object",
786
+ "properties" => { "a" => {"type" => "string", "format" => "ipv6"}}
787
+ }
788
+
789
+ data = {"a" => "1111:2222:8888:9999:aaaa:cccc:eeee:ffff"}
790
+ assert(JSON::Validator.validate(schema,data))
791
+ data = {"a" => "1111:0:8888:0:0:0:eeee:ffff"}
792
+ assert(JSON::Validator.validate(schema,data))
793
+ data = {"a" => "1111:2222:8888::eeee:ffff"}
794
+ assert(JSON::Validator.validate(schema,data))
795
+ data = {"a" => "1111:2222:8888:99999:aaaa:cccc:eeee:ffff"}
796
+ assert(!JSON::Validator.validate(schema,data))
797
+ data = {"a" => "1111:2222:8888:9999:aaaa:cccc:eeee:gggg"}
798
+ assert(!JSON::Validator.validate(schema,data))
799
+ data = {"a" => "1111:2222::9999::cccc:eeee:ffff"}
800
+ assert(!JSON::Validator.validate(schema,data))
801
+ data = {"a" => "1111:2222:8888:9999:aaaa:cccc:eeee:ffff:bbbb"}
802
+ assert(!JSON::Validator.validate(schema,data))
803
+ end
804
+
805
+ def test_format_time
806
+ schema = {
807
+ "type" => "object",
808
+ "properties" => { "a" => {"type" => "string", "format" => "time"}}
809
+ }
810
+
811
+ data = {"a" => "12:00:00"}
812
+ assert(JSON::Validator.validate(schema,data))
813
+ data = {"a" => "12:00"}
814
+ assert(!JSON::Validator.validate(schema,data))
815
+ data = {"a" => "12:00:60"}
816
+ assert(!JSON::Validator.validate(schema,data))
817
+ data = {"a" => "12:60:00"}
818
+ assert(!JSON::Validator.validate(schema,data))
819
+ data = {"a" => "24:00:00"}
820
+ assert(!JSON::Validator.validate(schema,data))
821
+ data = {"a" => "0:00:00"}
822
+ assert(!JSON::Validator.validate(schema,data))
823
+ data = {"a" => "-12:00:00"}
824
+ assert(!JSON::Validator.validate(schema,data))
825
+ data = {"a" => "12:00:00b"}
826
+ assert(!JSON::Validator.validate(schema,data))
827
+ end
828
+
829
+
830
+ def test_format_date
831
+ schema = {
832
+ "type" => "object",
833
+ "properties" => { "a" => {"type" => "string", "format" => "date"}}
834
+ }
835
+
836
+ data = {"a" => "2010-01-01"}
837
+ assert(JSON::Validator.validate(schema,data))
838
+ data = {"a" => "2010-01-32"}
839
+ assert(!JSON::Validator.validate(schema,data))
840
+ data = {"a" => "n2010-01-01"}
841
+ assert(!JSON::Validator.validate(schema,data))
842
+ data = {"a" => "2010-1-01"}
843
+ assert(!JSON::Validator.validate(schema,data))
844
+ data = {"a" => "2010-01-1"}
845
+ assert(!JSON::Validator.validate(schema,data))
846
+ data = {"a" => "2010-01-01n"}
847
+ assert(!JSON::Validator.validate(schema,data))
848
+ end
849
+
850
+ def test_format_datetime
851
+ schema = {
852
+ "type" => "object",
853
+ "properties" => { "a" => {"type" => "string", "format" => "date-time"}}
854
+ }
855
+
856
+ data = {"a" => "2010-01-01T12:00:00Z"}
857
+ assert(JSON::Validator.validate(schema,data))
858
+ data = {"a" => "2010-01-32T12:00:00Z"}
859
+ assert(!JSON::Validator.validate(schema,data))
860
+ data = {"a" => "2010-13-01T12:00:00Z"}
861
+ assert(!JSON::Validator.validate(schema,data))
862
+ data = {"a" => "2010-01-01T24:00:00Z"}
863
+ assert(!JSON::Validator.validate(schema,data))
864
+ data = {"a" => "2010-01-01T12:60:00Z"}
865
+ assert(!JSON::Validator.validate(schema,data))
866
+ data = {"a" => "2010-01-01T12:00:60Z"}
867
+ assert(!JSON::Validator.validate(schema,data))
868
+ data = {"a" => "2010-01-01T12:00:00"}
869
+ assert(!JSON::Validator.validate(schema,data))
870
+ data = {"a" => "2010-01-01T12:00:00z"}
871
+ assert(!JSON::Validator.validate(schema,data))
872
+ data = {"a" => "2010-01-0112:00:00Z"}
873
+ assert(!JSON::Validator.validate(schema,data))
874
+ end
875
+
876
+
877
+ def test_format_strings
878
+ data1 = {"a" => "boo"}
879
+ data2 = {"a" => 5}
880
+
881
+ schema = {
882
+ "type" => "object",
883
+ "properties" => { "a" => {"format" => "regex"}}
884
+ }
885
+ assert(JSON::Validator.validate(schema,data1))
886
+ assert(!JSON::Validator.validate(schema,data2))
887
+
888
+ schema = {
889
+ "type" => "object",
890
+ "properties" => { "a" => {"format" => "color"}}
891
+ }
892
+ assert(JSON::Validator.validate(schema,data1))
893
+ assert(!JSON::Validator.validate(schema,data2))
894
+
895
+ schema = {
896
+ "type" => "object",
897
+ "properties" => { "a" => {"format" => "style"}}
898
+ }
899
+ assert(JSON::Validator.validate(schema,data1))
900
+ assert(!JSON::Validator.validate(schema,data2))
901
+
902
+ schema = {
903
+ "type" => "object",
904
+ "properties" => { "a" => {"format" => "phone"}}
905
+ }
906
+ assert(JSON::Validator.validate(schema,data1))
907
+ assert(!JSON::Validator.validate(schema,data2))
908
+
909
+ schema = {
910
+ "type" => "object",
911
+ "properties" => { "a" => {"format" => "uri"}}
912
+ }
913
+ assert(JSON::Validator.validate(schema,data1))
914
+ assert(!JSON::Validator.validate(schema,data2))
915
+
916
+ schema = {
917
+ "type" => "object",
918
+ "properties" => { "a" => {"format" => "email"}}
919
+ }
920
+ assert(JSON::Validator.validate(schema,data1))
921
+ assert(!JSON::Validator.validate(schema,data2))
922
+
923
+ schema = {
924
+ "type" => "object",
925
+ "properties" => { "a" => {"format" => "host-name"}}
926
+ }
927
+ assert(JSON::Validator.validate(schema,data1))
928
+ assert(!JSON::Validator.validate(schema,data2))
929
+ end
930
+
931
+
932
+ def test_format_numeric
933
+ data1 = {"a" => "boo"}
934
+ data2 = {"a" => 5}
935
+ data3 = {"a" => 5.4}
936
+
937
+ schema = {
938
+ "type" => "object",
939
+ "properties" => { "a" => {"format" => "utc-millisec"}}
940
+ }
941
+ assert(!JSON::Validator.validate(schema,data1))
942
+ assert(JSON::Validator.validate(schema,data2))
943
+ assert(JSON::Validator.validate(schema,data3))
944
+ end
945
+
946
+
947
+
747
948
  end
748
949
 
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: json-schema
3
3
  version: !ruby/object:Gem::Version
4
- hash: 3
4
+ hash: 1
5
5
  prerelease: false
6
6
  segments:
7
7
  - 0
8
8
  - 1
9
- - 12
10
- version: 0.1.12
9
+ - 13
10
+ version: 0.1.13
11
11
  platform: ruby
12
12
  authors:
13
13
  - Kenny Hoxworth
@@ -15,7 +15,7 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2011-02-15 00:00:00 -05:00
18
+ date: 2011-02-16 00:00:00 -05:00
19
19
  default_executable:
20
20
  dependencies:
21
21
  - !ruby/object:Gem::Dependency
@@ -45,9 +45,9 @@ files:
45
45
  - lib/json-schema/uri/file.rb
46
46
  - lib/json-schema/validator.rb
47
47
  - lib/json-schema.rb
48
+ - README.textile
48
49
  - test/test_files.rb
49
50
  - test/test_jsonschema.rb
50
- - README.textile
51
51
  has_rdoc: true
52
52
  homepage: http://github.com/hoxworth/json-schema/tree/master
53
53
  licenses: []